github.com/vmware/govmomi@v0.37.2/govc/library/import.go (about)

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