github.com/vmware/govmomi@v0.51.0/cli/library/import.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package library
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"flag"
    11  	"fmt"
    12  	"net/url"
    13  	"os"
    14  	"path/filepath"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/vmware/govmomi/cli"
    19  	"github.com/vmware/govmomi/cli/flags"
    20  	"github.com/vmware/govmomi/ovf/importer"
    21  	"github.com/vmware/govmomi/vapi/library"
    22  	"github.com/vmware/govmomi/vim25/soap"
    23  )
    24  
    25  type item struct {
    26  	*flags.ClientFlag
    27  	*flags.OutputFlag
    28  	library.Item
    29  	library.Checksum
    30  
    31  	manifest bool
    32  	pull     bool
    33  }
    34  
    35  func init() {
    36  	cli.Register("library.import", &item{})
    37  }
    38  
    39  func (cmd *item) Register(ctx context.Context, f *flag.FlagSet) {
    40  	cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
    41  	cmd.ClientFlag.Register(ctx, f)
    42  
    43  	cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
    44  	cmd.OutputFlag.Register(ctx, f)
    45  
    46  	f.StringVar(&cmd.Name, "n", "", "Library item name")
    47  	f.StringVar(&cmd.Type, "t", "", "Library item type")
    48  	f.BoolVar(&cmd.manifest, "m", false, "Require ova manifest")
    49  	f.BoolVar(&cmd.pull, "pull", false, "Pull library item from http endpoint")
    50  	f.StringVar(&cmd.Checksum.Checksum, "c", "", "Checksum value to verify the pulled library item")
    51  	f.StringVar(&cmd.Checksum.Algorithm, "a", "SHA256", "Algorithm used to calculate the checksum. Possible values are: SHA1, MD5, SHA256 (default), SHA512")
    52  }
    53  
    54  func (cmd *item) Usage() string {
    55  	return "LIBRARY ITEM"
    56  }
    57  
    58  func (cmd *item) Description() string {
    59  	return `Import library items.
    60  
    61  Examples:
    62    govc library.import library_name file.ova
    63    govc library.import library_name file.ovf
    64    govc library.import library_name file.iso
    65    govc library.import library_id file.iso # Use library id if multiple libraries have the same name
    66    govc library.import library_name/item_name file.ova # update existing item
    67    govc library.import library_name http://example.com/file.ovf # download and push to vCenter
    68    govc library.import -pull library_name http://example.com/file.ova # direct pull from vCenter
    69    govc library.import -pull -c=<checksum> -a=<SHA1|MD5|SHA256|SHA512> library_name http://example.com/file.ova # direct pull from vCenter with checksum validation`
    70  }
    71  
    72  func (cmd *item) Process(ctx context.Context) error {
    73  	if err := cmd.ClientFlag.Process(ctx); err != nil {
    74  		return err
    75  	}
    76  	return cmd.OutputFlag.Process(ctx)
    77  }
    78  
    79  func (cmd *item) Run(ctx context.Context, f *flag.FlagSet) error {
    80  	if f.NArg() != 2 {
    81  		return flag.ErrHelp
    82  	}
    83  
    84  	// Checksums are verified after the file is uploaded to a server.
    85  	// Check the algorithm and fail early if it's not supported.
    86  	if cmd.pull && cmd.Checksum.Checksum != "" {
    87  		switch cmd.Checksum.Algorithm {
    88  		case "SHA1", "MD5", "SHA256", "SHA512":
    89  		default:
    90  			return fmt.Errorf("invalid checksum algorithm: %s", cmd.Checksum.Algorithm)
    91  		}
    92  	}
    93  
    94  	file := f.Arg(1)
    95  	base := filepath.Base(file)
    96  	ext := filepath.Ext(base)
    97  	mf := strings.Replace(base, ext, ".mf", 1)
    98  	kind := ""
    99  	client, err := cmd.Client()
   100  	if err != nil {
   101  		return err
   102  	}
   103  	opener := importer.Opener{Client: client}
   104  	var archive importer.Archive
   105  	archive = &importer.FileArchive{Path: file, Opener: opener}
   106  
   107  	manifest := make(map[string]*library.Checksum)
   108  	if cmd.Name == "" {
   109  		cmd.Name = strings.TrimSuffix(base, ext)
   110  	}
   111  
   112  	switch ext {
   113  	case ".ova":
   114  		archive = &importer.TapeArchive{Path: file, Opener: opener}
   115  		base = "*.ovf"
   116  		mf = "*.mf"
   117  		kind = library.ItemTypeOVF
   118  	case ".ovf":
   119  		kind = library.ItemTypeOVF
   120  	case ".iso":
   121  		kind = library.ItemTypeISO
   122  	}
   123  
   124  	if cmd.Type == "" {
   125  		cmd.Type = kind
   126  	}
   127  
   128  	if !cmd.pull && cmd.Type == library.ItemTypeOVF {
   129  		f, _, err := archive.Open(mf)
   130  		if err == nil {
   131  			sums, err := library.ReadManifest(f)
   132  			_ = f.Close()
   133  			if err != nil {
   134  				return err
   135  			}
   136  			manifest = sums
   137  		} else {
   138  			msg := fmt.Sprintf("manifest %q: %s", mf, err)
   139  			if cmd.manifest {
   140  				return errors.New(msg)
   141  			}
   142  			fmt.Fprintln(os.Stderr, msg)
   143  		}
   144  	}
   145  
   146  	c, err := cmd.RestClient()
   147  	if err != nil {
   148  		return err
   149  	}
   150  	cmd.KeepAlive(c)
   151  
   152  	m := library.NewManager(c)
   153  	res, err := flags.ContentLibraryResult(ctx, c, "", f.Arg(0))
   154  	if err != nil {
   155  		return err
   156  	}
   157  
   158  	switch t := res.GetResult().(type) {
   159  	case library.Library:
   160  		cmd.LibraryID = t.ID
   161  		cmd.ID, err = m.CreateLibraryItem(ctx, cmd.Item)
   162  		if err != nil {
   163  			return err
   164  		}
   165  	case library.Item:
   166  		cmd.Item = t
   167  	default:
   168  		return fmt.Errorf("%q is a %T", f.Arg(0), t)
   169  	}
   170  
   171  	session, err := m.CreateLibraryItemUpdateSession(ctx, library.Session{
   172  		LibraryItemID: cmd.ID,
   173  	})
   174  	if err != nil {
   175  		return err
   176  	}
   177  	if cmd.pull {
   178  		_, err = m.AddLibraryItemFileFromURI(ctx, session, filepath.Base(file), file, cmd.Checksum)
   179  		if err != nil {
   180  			return err
   181  		}
   182  
   183  		err = m.CompleteLibraryItemUpdateSession(ctx, session)
   184  		if err != nil {
   185  			return err
   186  		}
   187  
   188  		return m.WaitOnLibraryItemUpdateSession(ctx, session, 3*time.Second, nil)
   189  	}
   190  
   191  	upload := func(name string) error {
   192  		f, size, err := archive.Open(name)
   193  		if err != nil {
   194  			return err
   195  		}
   196  		defer f.Close()
   197  
   198  		if e, ok := f.(*importer.TapeArchiveEntry); ok {
   199  			name = e.Name // expand path.Match's (e.g. "*.ovf" -> "name.ovf")
   200  		}
   201  
   202  		info := library.UpdateFile{
   203  			Name:       name,
   204  			SourceType: "PUSH",
   205  			Checksum:   manifest[name],
   206  			Size:       size,
   207  		}
   208  
   209  		update, err := m.AddLibraryItemFile(ctx, session, info)
   210  		if err != nil {
   211  			return err
   212  		}
   213  
   214  		p := soap.DefaultUpload
   215  		p.ContentLength = size
   216  		u, err := url.Parse(update.UploadEndpoint.URI)
   217  		if err != nil {
   218  			return err
   219  		}
   220  		if cmd.TTY {
   221  			logger := cmd.ProgressLogger(fmt.Sprintf("Uploading %s... ", name))
   222  			p.Progress = logger
   223  			defer logger.Wait()
   224  		}
   225  		return c.Upload(ctx, f, u, &p)
   226  	}
   227  
   228  	if err = upload(base); err != nil {
   229  		return err
   230  	}
   231  
   232  	if cmd.Type == library.ItemTypeOVF {
   233  		o, err := importer.ReadOvf(base, archive)
   234  		if err != nil {
   235  			return err
   236  		}
   237  
   238  		e, err := importer.ReadEnvelope(o)
   239  		if err != nil {
   240  			return fmt.Errorf("failed to parse ovf: %s", err)
   241  		}
   242  
   243  		for i := range e.References {
   244  			if err = upload(e.References[i].Href); err != nil {
   245  				return err
   246  			}
   247  		}
   248  	}
   249  
   250  	return m.CompleteLibraryItemUpdateSession(ctx, session)
   251  }