github.com/vmware/govmomi@v0.51.0/cli/importx/ovf.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 importx
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"flag"
    11  	"fmt"
    12  
    13  	"github.com/vmware/govmomi/cli"
    14  	"github.com/vmware/govmomi/cli/flags"
    15  	"github.com/vmware/govmomi/fault"
    16  	"github.com/vmware/govmomi/find"
    17  	"github.com/vmware/govmomi/object"
    18  	"github.com/vmware/govmomi/ovf/importer"
    19  	"github.com/vmware/govmomi/property"
    20  	"github.com/vmware/govmomi/vim25/mo"
    21  	"github.com/vmware/govmomi/vim25/types"
    22  )
    23  
    24  type ovfx struct {
    25  	*flags.DatastoreFlag
    26  	*flags.HostSystemFlag
    27  	*flags.OutputFlag
    28  	*flags.ResourcePoolFlag
    29  	*flags.FolderFlag
    30  
    31  	*OptionsFlag
    32  
    33  	Importer importer.Importer
    34  
    35  	lease bool
    36  	net   string // No need for *flags.NetworkFlag here
    37  }
    38  
    39  func init() {
    40  	cli.Register("import.ovf", &ovfx{})
    41  }
    42  
    43  func (cmd *ovfx) Register(ctx context.Context, f *flag.FlagSet) {
    44  	cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx)
    45  	cmd.DatastoreFlag.Register(ctx, f)
    46  	cmd.HostSystemFlag, ctx = flags.NewHostSystemFlag(ctx)
    47  	cmd.HostSystemFlag.Register(ctx, f)
    48  	cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
    49  	cmd.OutputFlag.Register(ctx, f)
    50  	cmd.ResourcePoolFlag, ctx = flags.NewResourcePoolFlag(ctx)
    51  	cmd.ResourcePoolFlag.Register(ctx, f)
    52  	cmd.FolderFlag, ctx = flags.NewFolderFlag(ctx)
    53  	cmd.FolderFlag.Register(ctx, f)
    54  
    55  	cmd.OptionsFlag, ctx = newOptionsFlag(ctx)
    56  	cmd.OptionsFlag.Register(ctx, f)
    57  
    58  	f.StringVar(&cmd.Importer.Name, "name", "", "Name to use for new entity")
    59  	f.BoolVar(&cmd.Importer.VerifyManifest, "m", false, "Verify checksum of uploaded files against manifest (.mf)")
    60  	f.BoolVar(&cmd.Importer.Hidden, "hidden", false, "Enable hidden properties")
    61  	f.BoolVar(&cmd.lease, "lease", false, "Output NFC Lease only")
    62  	f.StringVar(&cmd.net, "net", "", "Network")
    63  }
    64  
    65  func (cmd *ovfx) Process(ctx context.Context) error {
    66  	if err := cmd.DatastoreFlag.Process(ctx); err != nil {
    67  		return err
    68  	}
    69  	if err := cmd.HostSystemFlag.Process(ctx); err != nil {
    70  		return err
    71  	}
    72  	if err := cmd.OutputFlag.Process(ctx); err != nil {
    73  		return err
    74  	}
    75  	if err := cmd.ResourcePoolFlag.Process(ctx); err != nil {
    76  		return err
    77  	}
    78  	if err := cmd.OptionsFlag.Process(ctx); err != nil {
    79  		return err
    80  	}
    81  	if err := cmd.FolderFlag.Process(ctx); err != nil {
    82  		return err
    83  	}
    84  	return nil
    85  }
    86  
    87  func (cmd *ovfx) Usage() string {
    88  	return "PATH_TO_OVF"
    89  }
    90  
    91  func (cmd *ovfx) Run(ctx context.Context, f *flag.FlagSet) error {
    92  	fpath, err := cmd.Prepare(f)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	archive := &importer.FileArchive{Path: fpath}
    98  	archive.Client = cmd.Importer.Client
    99  
   100  	cmd.Importer.Archive = archive
   101  
   102  	if err = cmd.Import(ctx, fpath); err != nil {
   103  		if fault.Is(err, &types.OvfNoHostNic{}) {
   104  			hint := "specify Network with '-net' or '-options'"
   105  			return fmt.Errorf("%s (%s)", err.Error(), hint)
   106  		}
   107  		return err
   108  	}
   109  	return nil
   110  }
   111  
   112  func (cmd *ovfx) Import(ctx context.Context, fpath string) error {
   113  	if cmd.net != "" {
   114  		if len(cmd.Options.NetworkMapping) == 0 {
   115  			env, err := importer.Spec(fpath, cmd.Importer.Archive, false, false)
   116  			if err != nil {
   117  				return err
   118  			}
   119  
   120  			cmd.Options.NetworkMapping = env.NetworkMapping
   121  		}
   122  
   123  		for i := range cmd.Options.NetworkMapping {
   124  			cmd.Options.NetworkMapping[i].Network = cmd.net
   125  		}
   126  	}
   127  
   128  	if cmd.lease {
   129  		_, lease, err := cmd.Importer.ImportVApp(ctx, fpath, cmd.Options)
   130  		if err != nil {
   131  			return err
   132  		}
   133  
   134  		o, err := lease.Properties(ctx)
   135  		if err != nil {
   136  			return err
   137  		}
   138  
   139  		return cmd.WriteResult(o)
   140  	}
   141  
   142  	moref, err := cmd.Importer.Import(ctx, fpath, cmd.Options)
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	vm := object.NewVirtualMachine(cmd.Importer.Client, *moref)
   148  	return cmd.Deploy(vm, cmd.OutputFlag)
   149  }
   150  
   151  func (cmd *ovfx) Prepare(f *flag.FlagSet) (string, error) {
   152  	var err error
   153  
   154  	args := f.Args()
   155  	if len(args) != 1 {
   156  		return "", errors.New("no file specified")
   157  	}
   158  
   159  	cmd.Importer.Log = cmd.OutputFlag.Log
   160  	cmd.Importer.Client, err = cmd.DatastoreFlag.Client()
   161  	if err != nil {
   162  		return "", err
   163  	}
   164  
   165  	cmd.Importer.Datacenter, err = cmd.DatastoreFlag.Datacenter()
   166  	if err != nil {
   167  		return "", err
   168  	}
   169  
   170  	cmd.Importer.Datastore, err = cmd.datastore()
   171  	if err != nil {
   172  		return "", err
   173  	}
   174  
   175  	cmd.Importer.ResourcePool, err = cmd.ResourcePoolIfSpecified()
   176  	if err != nil {
   177  		return "", err
   178  	}
   179  
   180  	host, err := cmd.HostSystemIfSpecified()
   181  	if err != nil {
   182  		return "", err
   183  	}
   184  
   185  	if cmd.Importer.ResourcePool == nil {
   186  		if host == nil {
   187  			cmd.Importer.ResourcePool, err = cmd.ResourcePoolFlag.ResourcePool()
   188  		} else {
   189  			cmd.Importer.ResourcePool, err = host.ResourcePool(context.TODO())
   190  		}
   191  		if err != nil {
   192  			return "", err
   193  		}
   194  	}
   195  
   196  	cmd.Importer.Finder, err = cmd.DatastoreFlag.Finder()
   197  	if err != nil {
   198  		return "", err
   199  	}
   200  
   201  	cmd.Importer.Host, err = cmd.HostSystemIfSpecified()
   202  	if err != nil {
   203  		return "", err
   204  	}
   205  
   206  	// The folder argument must not be set on a VM in a vApp, otherwise causes
   207  	// InvalidArgument fault: A specified parameter was not correct: pool
   208  	if cmd.Importer.ResourcePool.Reference().Type != "VirtualApp" {
   209  		cmd.Importer.Folder, err = cmd.FolderOrDefault("vm")
   210  		if err != nil {
   211  			return "", err
   212  		}
   213  	}
   214  
   215  	if cmd.Importer.Name == "" {
   216  		// Override name from options if specified
   217  		if cmd.Options.Name != nil {
   218  			cmd.Importer.Name = *cmd.Options.Name
   219  		}
   220  	} else {
   221  		cmd.Options.Name = &cmd.Importer.Name
   222  	}
   223  
   224  	return f.Arg(0), nil
   225  }
   226  
   227  func (f *ovfx) datastore() (*object.Datastore, error) {
   228  	ctx := context.Background()
   229  
   230  	ds, err := f.Datastore()
   231  	if err == nil {
   232  		return ds, nil
   233  	}
   234  	if _, ok := err.(*find.NotFoundError); !ok {
   235  		return nil, err
   236  	}
   237  
   238  	finder, err := f.DatastoreFlag.Finder()
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  
   243  	pod, err := finder.DatastoreCluster(ctx, f.Name)
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  
   248  	var folder mo.Folder
   249  
   250  	err = pod.Properties(ctx, pod.Reference(), []string{"childEntity"}, &folder)
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	if len(folder.ChildEntity) == 0 {
   256  		return nil, fmt.Errorf("datastore cluster %q has no datastores", f.Name)
   257  	}
   258  
   259  	pc := property.DefaultCollector(pod.Client())
   260  
   261  	var stores []mo.Datastore
   262  
   263  	err = pc.Retrieve(ctx, folder.ChildEntity, []string{"info.freeSpace"}, &stores)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	// choose Datastore from DatastoreCluster (StoragePod) with the most free space
   269  	var ref types.ManagedObjectReference
   270  	var max int64
   271  
   272  	for _, ds := range stores {
   273  		space := ds.Info.GetDatastoreInfo().FreeSpace
   274  		if space > max {
   275  			max = space
   276  			ref = ds.Reference()
   277  		}
   278  	}
   279  
   280  	return object.NewDatastore(pod.Client(), ref), nil
   281  }