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

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