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 }