From c7e5aa989aa8a4af9932797e9f8ecd8b11b92082 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Mon, 22 Jul 2024 23:47:15 +0000 Subject: [PATCH] Update vendored dependencies --- go.mod | 4 +- go.sum | 8 +- .../vmware/govmomi/govc/flags/output.go | 142 ++------ .../vmware/govmomi/govc/importx/options.go | 74 +--- .../vmware/govmomi/govc/importx/ova.go | 15 +- .../vmware/govmomi/govc/importx/ovf.go | 337 ++---------------- .../vmware/govmomi/govc/importx/spec.go | 139 +------- .../vmware/govmomi/govc/vm/clone.go | 11 +- .../vmware/govmomi/govc/vm/create.go | 16 +- .../vmware/govmomi/govc/vm/migrate.go | 42 ++- .../govmomi/internal/version/version.go | 2 +- .../govmomi/object/virtual_device_list.go | 77 +++- .../govmomi/object/virtual_disk_manager.go | 27 ++ .../object/vm_compatability_checker.go | 111 ++++++ .../govmomi/object/vm_provisioning_checker.go | 67 ++++ .../{govc/importx => ovf/importer}/archive.go | 53 +-- .../importx => ovf/importer}/importable.go | 6 +- .../vmware/govmomi/ovf/importer/importer.go | 326 +++++++++++++++++ .../vmware/govmomi/ovf/importer/options.go | 91 +++++ .../vmware/govmomi/ovf/importer/spec.go | 138 +++++++ .../govmomi/vapi/library/finder/path.go | 50 +++ .../vmware/govmomi/vim25/mo/type_info.go | 26 +- .../vmware/govmomi/vim25/progress/loger.go | 125 +++++++ .../vmware/govmomi/vim25/types/types.go | 4 +- .../vmware/govmomi/vmdk/disk_info.go | 164 +++++++++ .../github.com/vmware/govmomi/vmdk/import.go | 20 +- vendor/modules.txt | 7 +- 27 files changed, 1375 insertions(+), 707 deletions(-) create mode 100644 vendor/github.com/vmware/govmomi/object/vm_compatability_checker.go create mode 100644 vendor/github.com/vmware/govmomi/object/vm_provisioning_checker.go rename vendor/github.com/vmware/govmomi/{govc/importx => ovf/importer}/archive.go (69%) rename vendor/github.com/vmware/govmomi/{govc/importx => ovf/importer}/importable.go (91%) create mode 100644 vendor/github.com/vmware/govmomi/ovf/importer/importer.go create mode 100644 vendor/github.com/vmware/govmomi/ovf/importer/options.go create mode 100644 vendor/github.com/vmware/govmomi/ovf/importer/spec.go create mode 100644 vendor/github.com/vmware/govmomi/vim25/progress/loger.go create mode 100644 vendor/github.com/vmware/govmomi/vmdk/disk_info.go diff --git a/go.mod b/go.mod index b559c7dd..389c8ad2 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/onsi/gomega v1.33.1 github.com/packer-community/winrmcp v0.0.0-20221126162354-6e900dd2c68f github.com/pkg/errors v0.9.1 - github.com/vmware/govmomi v0.38.0 + github.com/vmware/govmomi v0.39.0 golang.org/x/sys v0.22.0 ) @@ -30,7 +30,7 @@ require ( github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da // indirect + github.com/google/pprof v0.0.0-20240722153945-304e4f0156b8 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 3b99d0be..244ed835 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da h1:xRmpO92tb8y+Z85iUOMOicpCfaYcv7o3Cg3wKrIpg8g= -github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20240722153945-304e4f0156b8 h1:ssNFCCVmib/GQSzx3uCWyfMgOamLGWuGqlMS77Y1m3Y= +github.com/google/pprof v0.0.0-20240722153945-304e4f0156b8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -123,8 +123,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde h1:AMNpJRc7P+GTwVbl8DkK2I9I8BBUzNiHuH/tlxrpan0= github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde/go.mod h1:MvrEmduDUz4ST5pGZ7CABCnOU5f3ZiOAZzT6b1A6nX8= -github.com/vmware/govmomi v0.38.0 h1:UvQpLAOjDpO0JUxoPCXnEzOlEa/9kejO6K58qOFr6cM= -github.com/vmware/govmomi v0.38.0/go.mod h1:mtGWtM+YhTADHlCgJBiskSRPOZRsN9MSjPzaZLte/oQ= +github.com/vmware/govmomi v0.39.0 h1:soLZ08Q2zvjRSinNup8xVlw0KDDCJPPA1rIDmBhi7As= +github.com/vmware/govmomi v0.39.0/go.mod h1:oHzAQ1r6152zYDGcUqeK+EO8LhKo5wjtvWZBGHws2Hc= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= diff --git a/vendor/github.com/vmware/govmomi/govc/flags/output.go b/vendor/github.com/vmware/govmomi/govc/flags/output.go index c0338b18..8975285e 100644 --- a/vendor/github.com/vmware/govmomi/govc/flags/output.go +++ b/vendor/github.com/vmware/govmomi/govc/flags/output.go @@ -1,11 +1,11 @@ /* -Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved. +Copyright (c) 2014-2024 VMware, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -26,11 +26,11 @@ import ( "os" "reflect" "strings" - "sync" "time" "github.com/dougm/pretty" + "github.com/vmware/govmomi/govc/cli" "github.com/vmware/govmomi/task" "github.com/vmware/govmomi/vim25/progress" "github.com/vmware/govmomi/vim25/soap" @@ -50,6 +50,7 @@ type OutputFlag struct { TTY bool Dump bool Out io.Writer + Spec bool formatError bool formatIndent bool @@ -72,6 +73,9 @@ func (flag *OutputFlag) Register(ctx context.Context, f *flag.FlagSet) { f.BoolVar(&flag.JSON, "json", false, "Enable JSON output") f.BoolVar(&flag.XML, "xml", false, "Enable XML output") f.BoolVar(&flag.Dump, "dump", false, "Enable Go output") + if cli.ShowUnreleased() { + f.BoolVar(&flag.Spec, "spec", false, "Output spec without sending request") + } // Avoid adding more flags for now.. flag.formatIndent = os.Getenv("GOVC_INDENT") != "false" // Default to indented output flag.formatError = os.Getenv("GOVC_FORMAT_ERROR") != "false" // Default to formatted errors @@ -159,6 +163,25 @@ func dumpValue(val interface{}) interface{} { return val } +type outputAny struct { + Value any +} + +func (*outputAny) Write(io.Writer) error { + return nil +} + +func (a *outputAny) Dump() interface{} { + return a.Value +} + +func (flag *OutputFlag) WriteAny(val any) error { + if !flag.All() { + flag.XML = true + } + return flag.WriteResult(&outputAny{val}) +} + func (flag *OutputFlag) WriteResult(result OutputWriter) error { var err error @@ -204,7 +227,7 @@ type errorOutput struct { } func (e errorOutput) Write(w io.Writer) error { - reason := e.error.Error() + reason := e.Error() var messages []string var faults []types.LocalizableMessage @@ -261,15 +284,15 @@ func (e errorOutput) canEncode() bool { return soap.IsSoapFault(e.error) || soap.IsVimFault(e.error) } -// cannotEncode causes cli.Run to output err.Error() as it would without an error format specified -var cannotEncode = errors.New("cannot encode error") +// errCannotEncode causes cli.Run to output err.Error() as it would without an error format specified +var errCannotEncode = errors.New("cannot encode error") func (e errorOutput) MarshalJSON() ([]byte, error) { _, ok := e.error.(json.Marshaler) if ok || e.canEncode() { return json.Marshal(e.error) } - return nil, cannotEncode + return nil, errCannotEncode } func (e errorOutput) MarshalXML(encoder *xml.Encoder, start xml.StartElement) error { @@ -277,108 +300,9 @@ func (e errorOutput) MarshalXML(encoder *xml.Encoder, start xml.StartElement) er if ok || e.canEncode() { return encoder.Encode(e.error) } - return cannotEncode -} - -type progressLogger struct { - flag *OutputFlag - prefix string - - wg sync.WaitGroup - - sink chan chan progress.Report - done chan struct{} -} - -func newProgressLogger(flag *OutputFlag, prefix string) *progressLogger { - p := &progressLogger{ - flag: flag, - prefix: prefix, - - sink: make(chan chan progress.Report), - done: make(chan struct{}), - } - - p.wg.Add(1) - - go p.loopA() - - return p -} - -// loopA runs before Sink() has been called. -func (p *progressLogger) loopA() { - var err error - - defer p.wg.Done() - - tick := time.NewTicker(100 * time.Millisecond) - defer tick.Stop() - - called := false - - for stop := false; !stop; { - select { - case ch := <-p.sink: - err = p.loopB(tick, ch) - stop = true - called = true - case <-p.done: - stop = true - case <-tick.C: - line := fmt.Sprintf("\r%s", p.prefix) - p.flag.Log(line) - } - } - - if err != nil && err != io.EOF { - p.flag.Log(fmt.Sprintf("\r%sError: %s\n", p.prefix, err)) - } else if called { - p.flag.Log(fmt.Sprintf("\r%sOK\n", p.prefix)) - } -} - -// loopA runs after Sink() has been called. -func (p *progressLogger) loopB(tick *time.Ticker, ch <-chan progress.Report) error { - var r progress.Report - var ok bool - var err error - - for ok = true; ok; { - select { - case r, ok = <-ch: - if !ok { - break - } - err = r.Error() - case <-tick.C: - line := fmt.Sprintf("\r%s", p.prefix) - if r != nil { - line += fmt.Sprintf("(%.0f%%", r.Percentage()) - detail := r.Detail() - if detail != "" { - line += fmt.Sprintf(", %s", detail) - } - line += ")" - } - p.flag.Log(line) - } - } - - return err -} - -func (p *progressLogger) Sink() chan<- progress.Report { - ch := make(chan progress.Report) - p.sink <- ch - return ch -} - -func (p *progressLogger) Wait() { - close(p.done) - p.wg.Wait() + return errCannotEncode } -func (flag *OutputFlag) ProgressLogger(prefix string) *progressLogger { - return newProgressLogger(flag, prefix) +func (flag *OutputFlag) ProgressLogger(prefix string) *progress.ProgressLogger { + return progress.NewProgressLogger(flag.Log, prefix) } diff --git a/vendor/github.com/vmware/govmomi/govc/importx/options.go b/vendor/github.com/vmware/govmomi/govc/importx/options.go index 3d8190ec..ddea6e38 100644 --- a/vendor/github.com/vmware/govmomi/govc/importx/options.go +++ b/vendor/github.com/vmware/govmomi/govc/importx/options.go @@ -1,11 +1,11 @@ /* -Copyright (c) 2015-2023 VMware, Inc. All Rights Reserved. +Copyright (c) 2015-2024 VMware, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -26,78 +26,12 @@ import ( "github.com/vmware/govmomi/govc/flags" "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/ovf" + "github.com/vmware/govmomi/ovf/importer" "github.com/vmware/govmomi/vim25/types" ) -type KeyValue struct { - Key string - Value string -} - -// case insensitive for Key + Value -func (kv *KeyValue) UnmarshalJSON(b []byte) error { - e := struct { - types.KeyValue - Key *string - Value *string - }{ - types.KeyValue{}, &kv.Key, &kv.Value, - } - - err := json.Unmarshal(b, &e) - if err != nil { - return err - } - - if kv.Key == "" { - kv.Key = e.KeyValue.Key // "key" - } - - if kv.Value == "" { - kv.Value = e.KeyValue.Value // "value" - } - - return nil -} - -type Property struct { - KeyValue - Spec *ovf.Property `json:",omitempty"` -} - -type Network struct { - Name string - Network string -} - -type Options struct { - AllDeploymentOptions []string `json:",omitempty"` - Deployment string `json:",omitempty"` - - AllDiskProvisioningOptions []string `json:",omitempty"` - DiskProvisioning string - - AllIPAllocationPolicyOptions []string `json:",omitempty"` - IPAllocationPolicy string - - AllIPProtocolOptions []string `json:",omitempty"` - IPProtocol string - - PropertyMapping []Property `json:",omitempty"` - - NetworkMapping []Network `json:",omitempty"` - - Annotation string `json:",omitempty"` - - MarkAsTemplate bool - PowerOn bool - InjectOvfEnv bool - WaitForIP bool - Name *string -} - type OptionsFlag struct { - Options Options + Options importer.Options path string } diff --git a/vendor/github.com/vmware/govmomi/govc/importx/ova.go b/vendor/github.com/vmware/govmomi/govc/importx/ova.go index 343cdcdd..67712050 100644 --- a/vendor/github.com/vmware/govmomi/govc/importx/ova.go +++ b/vendor/github.com/vmware/govmomi/govc/importx/ova.go @@ -1,11 +1,11 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +Copyright (c) 2014-2024 VMware, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -22,6 +22,7 @@ import ( "github.com/vmware/govmomi/govc/cli" "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/ovf/importer" "github.com/vmware/govmomi/vim25/types" ) @@ -43,21 +44,21 @@ func (cmd *ova) Run(ctx context.Context, f *flag.FlagSet) error { return err } - archive := &TapeArchive{Path: fpath} - archive.Client = cmd.Client + archive := &importer.TapeArchive{Path: fpath} + archive.Client = cmd.Importer.Client - cmd.Archive = archive + cmd.Importer.Archive = archive moref, err := cmd.Import(fpath) if err != nil { return err } - vm := object.NewVirtualMachine(cmd.Client, *moref) + vm := object.NewVirtualMachine(cmd.Importer.Client, *moref) return cmd.Deploy(vm, cmd.OutputFlag) } func (cmd *ova) Import(fpath string) (*types.ManagedObjectReference, error) { ovf := "*.ovf" - return cmd.ovfx.Import(ovf) + return cmd.Importer.Import(context.TODO(), ovf, cmd.Options) } diff --git a/vendor/github.com/vmware/govmomi/govc/importx/ovf.go b/vendor/github.com/vmware/govmomi/govc/importx/ovf.go index e1765f4c..c903fc9a 100644 --- a/vendor/github.com/vmware/govmomi/govc/importx/ovf.go +++ b/vendor/github.com/vmware/govmomi/govc/importx/ovf.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2023 VMware, Inc. All Rights Reserved. +Copyright (c) 2014-2024 VMware, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,23 +17,14 @@ limitations under the License. package importx import ( - "bytes" "context" "errors" "flag" - "fmt" - "path" - "strings" - "github.com/vmware/govmomi/find" "github.com/vmware/govmomi/govc/cli" "github.com/vmware/govmomi/govc/flags" - "github.com/vmware/govmomi/nfc" "github.com/vmware/govmomi/object" - "github.com/vmware/govmomi/ovf" - "github.com/vmware/govmomi/vim25" - "github.com/vmware/govmomi/vim25/soap" - "github.com/vmware/govmomi/vim25/types" + "github.com/vmware/govmomi/ovf/importer" ) type ovfx struct { @@ -43,17 +34,9 @@ type ovfx struct { *flags.ResourcePoolFlag *flags.FolderFlag - *ArchiveFlag *OptionsFlag - Name string - VerifyManifest bool - Hidden bool - - Client *vim25.Client - Datacenter *object.Datacenter - Datastore *object.Datastore - ResourcePool *object.ResourcePool + Importer importer.Importer } func init() { @@ -72,14 +55,12 @@ func (cmd *ovfx) Register(ctx context.Context, f *flag.FlagSet) { cmd.FolderFlag, ctx = flags.NewFolderFlag(ctx) cmd.FolderFlag.Register(ctx, f) - cmd.ArchiveFlag, ctx = newArchiveFlag(ctx) - cmd.ArchiveFlag.Register(ctx, f) cmd.OptionsFlag, ctx = newOptionsFlag(ctx) cmd.OptionsFlag.Register(ctx, f) - f.StringVar(&cmd.Name, "name", "", "Name to use for new entity") - f.BoolVar(&cmd.VerifyManifest, "m", false, "Verify checksum of uploaded files against manifest (.mf)") - f.BoolVar(&cmd.Hidden, "hidden", false, "Enable hidden properties") + f.StringVar(&cmd.Importer.Name, "name", "", "Name to use for new entity") + f.BoolVar(&cmd.Importer.VerifyManifest, "m", false, "Verify checksum of uploaded files against manifest (.mf)") + f.BoolVar(&cmd.Importer.Hidden, "hidden", false, "Enable hidden properties") } func (cmd *ovfx) Process(ctx context.Context) error { @@ -95,9 +76,6 @@ func (cmd *ovfx) Process(ctx context.Context) error { if err := cmd.ResourcePoolFlag.Process(ctx); err != nil { return err } - if err := cmd.ArchiveFlag.Process(ctx); err != nil { - return err - } if err := cmd.OptionsFlag.Process(ctx); err != nil { return err } @@ -117,17 +95,17 @@ func (cmd *ovfx) Run(ctx context.Context, f *flag.FlagSet) error { return err } - archive := &FileArchive{Path: fpath} - archive.Client = cmd.Client + archive := &importer.FileArchive{Path: fpath} + archive.Client = cmd.Importer.Client - cmd.Archive = archive + cmd.Importer.Archive = archive - moref, err := cmd.Import(fpath) + moref, err := cmd.Importer.Import(context.TODO(), fpath, cmd.Options) if err != nil { return err } - vm := object.NewVirtualMachine(cmd.Client, *moref) + vm := object.NewVirtualMachine(cmd.Importer.Client, *moref) return cmd.Deploy(vm, cmd.OutputFlag) } @@ -139,313 +117,70 @@ func (cmd *ovfx) Prepare(f *flag.FlagSet) (string, error) { return "", errors.New("no file specified") } - cmd.Client, err = cmd.DatastoreFlag.Client() + cmd.Importer.Log = cmd.OutputFlag.Log + cmd.Importer.Client, err = cmd.DatastoreFlag.Client() if err != nil { return "", err } - cmd.Datacenter, err = cmd.DatastoreFlag.Datacenter() + cmd.Importer.Datacenter, err = cmd.DatastoreFlag.Datacenter() if err != nil { return "", err } - cmd.Datastore, err = cmd.DatastoreFlag.Datastore() + cmd.Importer.Datastore, err = cmd.DatastoreFlag.Datastore() if err != nil { return "", err } - cmd.ResourcePool, err = cmd.ResourcePoolFlag.ResourcePoolIfSpecified() + cmd.Importer.ResourcePool, err = cmd.ResourcePoolIfSpecified() if err != nil { return "", err } - return f.Arg(0), nil -} - -func (cmd *ovfx) Map(op []Property) (p []types.KeyValue) { - for _, v := range op { - p = append(p, types.KeyValue{ - Key: v.Key, - Value: v.Value, - }) - } - - return -} - -func (cmd *ovfx) validateNetwork(e *ovf.Envelope, net Network) { - var names []string - - if e.Network != nil { - for _, n := range e.Network.Networks { - if n.Name == net.Name { - return - } - names = append(names, n.Name) - } - } - - _, _ = cmd.Log(fmt.Sprintf("Warning: invalid NetworkMapping.Name=%q, valid names=%s\n", net.Name, names)) -} - -func (cmd *ovfx) NetworkMap(e *ovf.Envelope) ([]types.OvfNetworkMapping, error) { - ctx := context.TODO() - finder, err := cmd.DatastoreFlag.Finder() - if err != nil { - return nil, err - } - - var nmap []types.OvfNetworkMapping - for _, m := range cmd.Options.NetworkMapping { - if m.Network == "" { - continue // Not set, let vSphere choose the default network - } - cmd.validateNetwork(e, m) - - var ref types.ManagedObjectReference - - net, err := finder.Network(ctx, m.Network) - if err != nil { - switch err.(type) { - case *find.NotFoundError: - if !ref.FromString(m.Network) { - return nil, err - } // else this is a raw MO ref - default: - return nil, err - } - } else { - ref = net.Reference() - } - - nmap = append(nmap, types.OvfNetworkMapping{ - Name: m.Name, - Network: ref, - }) - } - - return nmap, err -} - -func (cmd *ovfx) Import(fpath string) (*types.ManagedObjectReference, error) { - ctx := context.TODO() - - o, err := cmd.ReadOvf(fpath) - if err != nil { - return nil, err - } - - e, err := cmd.ReadEnvelope(o) - if err != nil { - return nil, fmt.Errorf("failed to parse ovf: %s", err) - } - - name := "Govc Virtual Appliance" - if e.VirtualSystem != nil { - name = e.VirtualSystem.ID - if e.VirtualSystem.Name != nil { - name = *e.VirtualSystem.Name - } - - if cmd.Hidden { - // TODO: userConfigurable is optional and defaults to false, so we should *add* userConfigurable=true - // if not set for a Property. But, there'd be a bunch more work involved to preserve other data in doing - // a complete xml.Marshal of the .ovf - o = bytes.ReplaceAll(o, []byte(`userConfigurable="false"`), []byte(`userConfigurable="true"`)) - } - } - - // Override name from options if specified - if cmd.Options.Name != nil { - name = *cmd.Options.Name - } - - // Override name from arguments if specified - if cmd.Name != "" { - name = cmd.Name - } - - nmap, err := cmd.NetworkMap(e) - if err != nil { - return nil, err - } - - cisp := types.OvfCreateImportSpecParams{ - DiskProvisioning: cmd.Options.DiskProvisioning, - EntityName: name, - IpAllocationPolicy: cmd.Options.IPAllocationPolicy, - IpProtocol: cmd.Options.IPProtocol, - OvfManagerCommonParams: types.OvfManagerCommonParams{ - DeploymentOption: cmd.Options.Deployment, - Locale: "US"}, - PropertyMapping: cmd.Map(cmd.Options.PropertyMapping), - NetworkMapping: nmap, - } - host, err := cmd.HostSystemIfSpecified() if err != nil { - return nil, err + return "", err } - if cmd.ResourcePool == nil { + if cmd.Importer.ResourcePool == nil { if host == nil { - cmd.ResourcePool, err = cmd.ResourcePoolFlag.ResourcePool() + cmd.Importer.ResourcePool, err = cmd.ResourcePoolFlag.ResourcePool() } else { - cmd.ResourcePool, err = host.ResourcePool(ctx) + cmd.Importer.ResourcePool, err = host.ResourcePool(context.TODO()) } if err != nil { - return nil, err + return "", err } } - m := ovf.NewManager(cmd.Client) - spec, err := m.CreateImportSpec(ctx, string(o), cmd.ResourcePool, cmd.Datastore, cisp) + cmd.Importer.Finder, err = cmd.DatastoreFlag.Finder() if err != nil { - return nil, err - } - if spec.Error != nil { - return nil, errors.New(spec.Error[0].LocalizedMessage) - } - if spec.Warning != nil { - for _, w := range spec.Warning { - _, _ = cmd.Log(fmt.Sprintf("Warning: %s\n", w.LocalizedMessage)) - } + return "", err } - if cmd.Options.Annotation != "" { - switch s := spec.ImportSpec.(type) { - case *types.VirtualMachineImportSpec: - s.ConfigSpec.Annotation = cmd.Options.Annotation - case *types.VirtualAppImportSpec: - s.VAppConfigSpec.Annotation = cmd.Options.Annotation - } + cmd.Importer.Host, err = cmd.HostSystemIfSpecified() + if err != nil { + return "", err } - var folder *object.Folder // The folder argument must not be set on a VM in a vApp, otherwise causes // InvalidArgument fault: A specified parameter was not correct: pool - if cmd.ResourcePool.Reference().Type != "VirtualApp" { - folder, err = cmd.FolderOrDefault("vm") + if cmd.Importer.ResourcePool.Reference().Type != "VirtualApp" { + cmd.Importer.Folder, err = cmd.FolderOrDefault("vm") if err != nil { - return nil, err + return "", err } } - if cmd.VerifyManifest { - err = cmd.readManifest(fpath) - if err != nil { - return nil, err + if cmd.Importer.Name == "" { + // Override name from options if specified + if cmd.Options.Name != nil { + cmd.Importer.Name = *cmd.Options.Name } + } else { + cmd.Options.Name = &cmd.Importer.Name } - lease, err := cmd.ResourcePool.ImportVApp(ctx, spec.ImportSpec, folder, host) - if err != nil { - return nil, err - } - - info, err := lease.Wait(ctx, spec.FileItem) - if err != nil { - return nil, err - } - - u := lease.StartUpdater(ctx, info) - defer u.Done() - - for _, i := range info.Items { - err = cmd.Upload(ctx, lease, i) - if err != nil { - return nil, err - } - } - - return &info.Entity, lease.Complete(ctx) -} - -func (cmd *ovfx) Upload(ctx context.Context, lease *nfc.Lease, item nfc.FileItem) error { - file := item.Path - - f, size, err := cmd.Open(file) - if err != nil { - return err - } - defer f.Close() - - logger := cmd.ProgressLogger(fmt.Sprintf("Uploading %s... ", path.Base(file))) - defer logger.Wait() - - opts := soap.Upload{ - ContentLength: size, - Progress: logger, - } - - err = lease.Upload(ctx, item, f, opts) - if err != nil { - return err - } - - if cmd.VerifyManifest { - mapImportKeyToKey := func(urls []types.HttpNfcLeaseDeviceUrl, importKey string) string { - for _, url := range urls { - if url.ImportKey == importKey { - return url.Key - } - } - return "" - } - leaseInfo, err := lease.Wait(ctx, nil) - if err != nil { - return err - } - return cmd.validateChecksum(ctx, lease, file, mapImportKeyToKey(leaseInfo.DeviceUrl, item.DeviceId)) - } - return nil -} - -func (cmd *ovfx) validateChecksum(ctx context.Context, lease *nfc.Lease, file string, key string) error { - sum, found := cmd.manifest[file] - if !found { - msg := fmt.Sprintf("missing checksum for %v in manifest file", file) - return errors.New(msg) - } - // Perform the checksum match eagerly, after each file upload, instead - // of after uploading all the files, to provide fail-fast behavior. - // (Trade-off here is multiple GetManifest() API calls to the server.) - manifests, err := lease.GetManifest(ctx) - if err != nil { - return err - } - for _, m := range manifests { - if m.Key == key { - // Compare server-side computed checksum of uploaded file - // against the client's manifest entry (assuming client's - // manifest has correct checksums - client doesn't compute - // checksum of the file before uploading). - - // Try matching sha1 first (newer versions have moved to sha256). - if strings.ToUpper(sum.Algorithm) == "SHA1" { - if sum.Checksum != m.Sha1 { - msg := fmt.Sprintf("manifest checksum %v mismatch with uploaded checksum %v for file %v", - sum.Checksum, m.Sha1, file) - return errors.New(msg) - } - // Uploaded file checksum computed by server matches with local manifest entry. - return nil - } - // If not sha1, check for other types (in a separate field). - if !strings.EqualFold(sum.Algorithm, m.ChecksumType) { - msg := fmt.Sprintf("manifest checksum type %v mismatch with uploaded checksum type %v for file %v", - sum.Algorithm, m.ChecksumType, file) - return errors.New(msg) - } - if !strings.EqualFold(sum.Checksum, m.Checksum) { - msg := fmt.Sprintf("manifest checksum %v mismatch with uploaded checksum %v for file %v", - sum.Checksum, m.Checksum, file) - return errors.New(msg) - } - // Uploaded file checksum computed by server matches with local manifest entry. - return nil - } - } - msg := fmt.Sprintf("missing manifest entry on server for uploaded file %v (key %v), manifests=%#v", file, key, manifests) - return errors.New(msg) + return f.Arg(0), nil } diff --git a/vendor/github.com/vmware/govmomi/govc/importx/spec.go b/vendor/github.com/vmware/govmomi/govc/importx/spec.go index acb1729f..01dcf0b6 100644 --- a/vendor/github.com/vmware/govmomi/govc/importx/spec.go +++ b/vendor/github.com/vmware/govmomi/govc/importx/spec.go @@ -22,27 +22,18 @@ import ( "fmt" "io" "path" - "strings" "github.com/vmware/govmomi/govc/cli" "github.com/vmware/govmomi/govc/flags" - "github.com/vmware/govmomi/ovf" - "github.com/vmware/govmomi/vim25/types" -) - -var ( - allDiskProvisioningOptions = types.OvfCreateImportSpecParamsDiskProvisioningType("").Strings() - - allIPAllocationPolicyOptions = types.VAppIPAssignmentInfoIpAllocationPolicy("").Strings() - - allIPProtocolOptions = types.VAppIPAssignmentInfoProtocols("").Strings() + "github.com/vmware/govmomi/ovf/importer" ) type spec struct { - *ArchiveFlag *flags.ClientFlag *flags.OutputFlag + Archive importer.Archive + hidden bool } @@ -51,8 +42,6 @@ func init() { } func (cmd *spec) Register(ctx context.Context, f *flag.FlagSet) { - cmd.ArchiveFlag, ctx = newArchiveFlag(ctx) - cmd.ArchiveFlag.Register(ctx, f) cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) cmd.ClientFlag.Register(ctx, f) @@ -63,9 +52,6 @@ func (cmd *spec) Register(ctx context.Context, f *flag.FlagSet) { } func (cmd *spec) Process(ctx context.Context) error { - if err := cmd.ArchiveFlag.Process(ctx); err != nil { - return err - } if err := cmd.ClientFlag.Process(ctx); err != nil { return err } @@ -86,29 +72,29 @@ func (cmd *spec) Run(ctx context.Context, f *flag.FlagSet) error { if len(fpath) > 0 { switch path.Ext(fpath) { case ".ovf": - cmd.Archive = &FileArchive{Path: fpath} + cmd.Archive = &importer.FileArchive{Path: fpath} case "", ".ova": - cmd.Archive = &TapeArchive{Path: fpath} + cmd.Archive = &importer.TapeArchive{Path: fpath} fpath = "*.ovf" default: return fmt.Errorf("invalid file extension %s", path.Ext(fpath)) } - if isRemotePath(f.Arg(0)) { + if importer.IsRemotePath(f.Arg(0)) { client, err := cmd.Client() if err != nil { return err } switch archive := cmd.Archive.(type) { - case *FileArchive: + case *importer.FileArchive: archive.Client = client - case *TapeArchive: + case *importer.TapeArchive: archive.Client = client } } } - env, err := cmd.Spec(fpath) + env, err := importer.Spec(fpath, cmd.Archive, cmd.hidden, cmd.Verbose()) if err != nil { return err } @@ -120,114 +106,9 @@ func (cmd *spec) Run(ctx context.Context, f *flag.FlagSet) error { } type specResult struct { - *Options + *importer.Options } func (*specResult) Write(w io.Writer) error { return nil } - -func (cmd *spec) Map(e *ovf.Envelope) (res []Property) { - if e == nil || e.VirtualSystem == nil { - return nil - } - - for _, p := range e.VirtualSystem.Product { - for i, v := range p.Property { - if v.UserConfigurable == nil { - continue - } - if !*v.UserConfigurable && !cmd.hidden { - continue - } - - d := "" - if v.Default != nil { - d = *v.Default - } - - // vSphere only accept True/False as boolean values for some reason - if v.Type == "boolean" { - d = strings.Title(d) - } - - np := Property{KeyValue: KeyValue{Key: p.Key(v), Value: d}} - - if cmd.Verbose() { - np.Spec = &p.Property[i] - } - - res = append(res, np) - } - } - - return -} - -func (cmd *spec) Spec(fpath string) (*Options, error) { - e := &ovf.Envelope{} - if fpath != "" { - d, err := cmd.ReadOvf(fpath) - if err != nil { - return nil, err - } - - if e, err = cmd.ReadEnvelope(d); err != nil { - return nil, err - } - } - - var deploymentOptions []string - if e.DeploymentOption != nil && e.DeploymentOption.Configuration != nil { - // add default first - for _, c := range e.DeploymentOption.Configuration { - if c.Default != nil && *c.Default { - deploymentOptions = append(deploymentOptions, c.ID) - } - } - - for _, c := range e.DeploymentOption.Configuration { - if c.Default == nil || !*c.Default { - deploymentOptions = append(deploymentOptions, c.ID) - } - } - } - - o := Options{ - DiskProvisioning: allDiskProvisioningOptions[0], - IPAllocationPolicy: allIPAllocationPolicyOptions[0], - IPProtocol: allIPProtocolOptions[0], - MarkAsTemplate: false, - PowerOn: false, - WaitForIP: false, - InjectOvfEnv: false, - PropertyMapping: cmd.Map(e), - } - - if deploymentOptions != nil { - o.Deployment = deploymentOptions[0] - } - - if e.VirtualSystem != nil && e.VirtualSystem.Annotation != nil { - for _, a := range e.VirtualSystem.Annotation { - o.Annotation += a.Annotation - } - } - - if e.Network != nil { - for _, net := range e.Network.Networks { - o.NetworkMapping = append(o.NetworkMapping, Network{net.Name, ""}) - } - } - - if cmd.Verbose() { - if deploymentOptions != nil { - o.AllDeploymentOptions = deploymentOptions - } - o.AllDiskProvisioningOptions = allDiskProvisioningOptions - o.AllIPAllocationPolicyOptions = allIPAllocationPolicyOptions - o.AllIPProtocolOptions = allIPProtocolOptions - } - - return &o, nil -} diff --git a/vendor/github.com/vmware/govmomi/govc/vm/clone.go b/vendor/github.com/vmware/govmomi/govc/vm/clone.go index d9ef49a2..0e85970e 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/clone.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/clone.go @@ -1,11 +1,11 @@ /* -Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved. +Copyright (c) 2016-2024 VMware, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -243,6 +243,9 @@ func (cmd *clone) Run(ctx context.Context, f *flag.FlagSet) error { if err != nil { return err } + if cmd.Spec { + return nil + } if cmd.cpus > 0 || cmd.memory > 0 || cmd.annotation != "" { vmConfigSpec := types.VirtualMachineConfigSpec{} @@ -471,6 +474,10 @@ func (cmd *clone) cloneVM(ctx context.Context) (*object.VirtualMachine, error) { cloneSpec.Customization = &customSpec } + if cmd.Spec { + return nil, cmd.WriteAny(cloneSpec) + } + task, err := cmd.VirtualMachine.Clone(ctx, cmd.Folder, cmd.name, *cloneSpec) if err != nil { return nil, err diff --git a/vendor/github.com/vmware/govmomi/govc/vm/create.go b/vendor/github.com/vmware/govmomi/govc/vm/create.go index 46c3a57d..757fc2ea 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/create.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/create.go @@ -307,7 +307,7 @@ func (cmd *create) Run(ctx context.Context, f *flag.FlagSet) error { if err != nil { return err } - if cmd.place { + if cmd.place || cmd.Spec { return nil } info, err := task.WaitForResult(ctx, nil) @@ -490,7 +490,7 @@ func (cmd *create) createVM(ctx context.Context) (*object.Task, error) { return nil, fmt.Errorf("please provide either a cluster, datastore or datastore-cluster") } - if !cmd.force { + if !cmd.force && !cmd.Spec { vmxPath := fmt.Sprintf("%s/%s.vmx", cmd.name, cmd.name) _, err := datastore.Stat(ctx, vmxPath) @@ -506,6 +506,10 @@ func (cmd *create) createVM(ctx context.Context) (*object.Task, error) { VmPathName: fmt.Sprintf("[%s]", datastore.Name()), } + if cmd.Spec { + return nil, cmd.WriteAny(spec) + } + return folder.CreateVM(ctx, *spec, cmd.ResourcePool, cmd.HostSystem) } @@ -519,6 +523,14 @@ func (cmd *create) addStorage(devices object.VirtualDeviceList) (object.VirtualD devices = append(devices, nvme) cmd.controller = devices.Name(nvme) + } else if cmd.controller == "sata" { + sata, err := devices.CreateSATAController() + if err != nil { + return nil, err + } + + devices = append(devices, sata) + cmd.controller = devices.Name(sata) } else { scsi, err := devices.CreateSCSIController(cmd.controller) if err != nil { diff --git a/vendor/github.com/vmware/govmomi/govc/vm/migrate.go b/vendor/github.com/vmware/govmomi/govc/vm/migrate.go index cf133e86..0e6d1a9f 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/migrate.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/migrate.go @@ -1,11 +1,11 @@ /* -Copyright (c) 2016 VMware, Inc. All Rights Reserved. +Copyright (c) 2016-2024 VMware, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -32,6 +32,7 @@ type migrate struct { *flags.ResourcePoolFlag *flags.HostSystemFlag *flags.DatastoreFlag + *flags.NetworkFlag *flags.VirtualMachineFlag priority types.VirtualMachineMovePriority @@ -58,6 +59,9 @@ func (cmd *migrate) Register(ctx context.Context, f *flag.FlagSet) { cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx) cmd.DatastoreFlag.Register(ctx, f) + cmd.NetworkFlag, ctx = flags.NewNetworkFlag(ctx) + cmd.NetworkFlag.Register(ctx, f) + f.StringVar((*string)(&cmd.priority), "priority", string(types.VirtualMachineMovePriorityDefaultPriority), "The task priority") } @@ -77,6 +81,9 @@ func (cmd *migrate) Process(ctx context.Context) error { if err := cmd.DatastoreFlag.Process(ctx); err != nil { return err } + if err := cmd.NetworkFlag.Process(ctx); err != nil { + return err + } return nil } @@ -95,7 +102,36 @@ Examples: } func (cmd *migrate) relocate(ctx context.Context, vm *object.VirtualMachine) error { - task, err := vm.Relocate(ctx, cmd.spec, cmd.priority) + spec := cmd.spec + + if cmd.NetworkFlag.IsSet() { + dev, err := cmd.NetworkFlag.Device() + if err != nil { + return err + } + + devices, err := vm.Device(ctx) + if err != nil { + return err + } + + net := devices.SelectByType((*types.VirtualEthernetCard)(nil)) + if len(net) != 1 { + return fmt.Errorf("-net specified, but %s has %d nics", vm.Name(), len(net)) + } + cmd.NetworkFlag.Change(net[0], dev) + + spec.DeviceChange = append(spec.DeviceChange, &types.VirtualDeviceConfigSpec{ + Device: net[0], + Operation: types.VirtualDeviceConfigSpecOperationEdit, + }) + } + + if cmd.VirtualMachineFlag.Spec { + return cmd.VirtualMachineFlag.WriteAny(spec) + } + + task, err := vm.Relocate(ctx, spec, cmd.priority) if err != nil { return err } diff --git a/vendor/github.com/vmware/govmomi/internal/version/version.go b/vendor/github.com/vmware/govmomi/internal/version/version.go index f1e0f3d0..c8c9969a 100644 --- a/vendor/github.com/vmware/govmomi/internal/version/version.go +++ b/vendor/github.com/vmware/govmomi/internal/version/version.go @@ -21,5 +21,5 @@ const ( ClientName = "govmomi" // ClientVersion is the version of this SDK - ClientVersion = "0.38.0" + ClientVersion = "0.39.0" ) diff --git a/vendor/github.com/vmware/govmomi/object/virtual_device_list.go b/vendor/github.com/vmware/govmomi/object/virtual_device_list.go index 92797dcd..295171af 100644 --- a/vendor/github.com/vmware/govmomi/object/virtual_device_list.go +++ b/vendor/github.com/vmware/govmomi/object/virtual_device_list.go @@ -361,6 +361,77 @@ func (l VirtualDeviceList) newNVMEBusNumber() int32 { return -1 } +// FindSATAController will find the named SATA or AHCI controller if given, otherwise will pick an available controller. +// An error is returned if the named controller is not found or not a SATA or AHCI controller. Or, if name is not +// given and no available controller can be found. +func (l VirtualDeviceList) FindSATAController(name string) (types.BaseVirtualController, error) { + if name != "" { + d := l.Find(name) + if d == nil { + return nil, fmt.Errorf("device '%s' not found", name) + } + switch c := d.(type) { + case *types.VirtualSATAController: + return c, nil + case *types.VirtualAHCIController: + return c, nil + default: + return nil, fmt.Errorf("%s is not a SATA or AHCI controller", name) + } + } + + c := l.PickController((*types.VirtualSATAController)(nil)) + if c == nil { + c = l.PickController((*types.VirtualAHCIController)(nil)) + } + if c == nil { + return nil, errors.New("no available SATA or AHCI controller") + } + + switch c := c.(type) { + case *types.VirtualSATAController: + return c, nil + case *types.VirtualAHCIController: + return c, nil + } + + return nil, errors.New("unexpected controller type") +} + +// CreateSATAController creates a new SATA controller. +func (l VirtualDeviceList) CreateSATAController() (types.BaseVirtualDevice, error) { + sata := &types.VirtualAHCIController{} + sata.BusNumber = l.newSATABusNumber() + sata.Key = l.NewKey() + + return sata, nil +} + +var sataBusNumbers = []int{0, 1, 2, 3} + +// newSATABusNumber returns the bus number to use for adding a new SATA bus device. +// -1 is returned if there are no bus numbers available. +func (l VirtualDeviceList) newSATABusNumber() int32 { + var used []int + + for _, d := range l.SelectByType((*types.VirtualSATAController)(nil)) { + num := d.(types.BaseVirtualController).GetVirtualController().BusNumber + if num >= 0 { + used = append(used, int(num)) + } // else caller is creating a new vm using SATAControllerTypes + } + + sort.Ints(used) + + for i, n := range sataBusNumbers { + if i == len(used) || n != used[i] { + return int32(n) + } + } + + return -1 +} + // FindDiskController will find an existing ide or scsi disk controller. func (l VirtualDeviceList) FindDiskController(name string) (types.BaseVirtualController, error) { switch { @@ -370,6 +441,8 @@ func (l VirtualDeviceList) FindDiskController(name string) (types.BaseVirtualCon return l.FindSCSIController("") case name == "nvme": return l.FindNVMEController("") + case name == "sata": + return l.FindSATAController("") default: if c, ok := l.Find(name).(types.BaseVirtualController); ok { return c, nil @@ -389,6 +462,8 @@ func (l VirtualDeviceList) PickController(kind types.BaseVirtualController) type return num < 15 case *types.VirtualIDEController: return num < 2 + case types.BaseVirtualSATAController: + return num < 30 case *types.VirtualNVMEController: return num < 8 default: @@ -909,8 +984,6 @@ func (l VirtualDeviceList) Type(device types.BaseVirtualDevice) string { return "pvscsi" case *types.VirtualLsiLogicSASController: return "lsilogic-sas" - case *types.VirtualNVMEController: - return "nvme" case *types.VirtualPrecisionClock: return "clock" default: diff --git a/vendor/github.com/vmware/govmomi/object/virtual_disk_manager.go b/vendor/github.com/vmware/govmomi/object/virtual_disk_manager.go index 72439caf..6724843e 100644 --- a/vendor/github.com/vmware/govmomi/object/virtual_disk_manager.go +++ b/vendor/github.com/vmware/govmomi/object/virtual_disk_manager.go @@ -94,6 +94,33 @@ func (m VirtualDiskManager) CreateVirtualDisk( return NewTask(m.c, res.Returnval), nil } +// ExtendVirtualDisk extends an existing virtual disk. +func (m VirtualDiskManager) ExtendVirtualDisk( + ctx context.Context, + name string, datacenter *Datacenter, + capacityKb int64, + eagerZero *bool) (*Task, error) { + + req := types.ExtendVirtualDisk_Task{ + This: m.Reference(), + Name: name, + NewCapacityKb: capacityKb, + EagerZero: eagerZero, + } + + if datacenter != nil { + ref := datacenter.Reference() + req.Datacenter = &ref + } + + res, err := methods.ExtendVirtualDisk_Task(ctx, m.c, &req) + if err != nil { + return nil, err + } + + return NewTask(m.c, res.Returnval), nil +} + // MoveVirtualDisk moves a virtual disk. func (m VirtualDiskManager) MoveVirtualDisk( ctx context.Context, diff --git a/vendor/github.com/vmware/govmomi/object/vm_compatability_checker.go b/vendor/github.com/vmware/govmomi/object/vm_compatability_checker.go new file mode 100644 index 00000000..8f70d780 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/object/vm_compatability_checker.go @@ -0,0 +1,111 @@ +/* +Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package object + +import ( + "context" + + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/types" +) + +// VmCompatibilityChecker models the CompatibilityChecker, a singleton managed +// object that can answer questions about compatibility of a virtual machine +// with a host. +// +// For more information, see: +// https://dp-downloads.broadcom.com/api-content/apis/API_VWSA_001/8.0U3/html/ReferenceGuides/vim.vm.check.CompatibilityChecker.html +type VmCompatibilityChecker struct { + Common +} + +func NewVmCompatibilityChecker(c *vim25.Client) *VmCompatibilityChecker { + return &VmCompatibilityChecker{ + Common: NewCommon(c, *c.ServiceContent.VmCompatibilityChecker), + } +} + +func (c VmCompatibilityChecker) CheckCompatibility( + ctx context.Context, + vm types.ManagedObjectReference, + host *types.ManagedObjectReference, + pool *types.ManagedObjectReference, + testTypes ...types.CheckTestType) ([]types.CheckResult, error) { + + req := types.CheckCompatibility_Task{ + This: c.Reference(), + Vm: vm, + Host: host, + Pool: pool, + TestType: checkTestTypesToStrings(testTypes), + } + + res, err := methods.CheckCompatibility_Task(ctx, c.c, &req) + if err != nil { + return nil, err + } + + ti, err := NewTask(c.c, res.Returnval).WaitForResult(ctx) + if err != nil { + return nil, err + } + + return ti.Result.(types.ArrayOfCheckResult).CheckResult, nil +} + +func (c VmCompatibilityChecker) CheckVmConfig( + ctx context.Context, + spec types.VirtualMachineConfigSpec, + vm *types.ManagedObjectReference, + host *types.ManagedObjectReference, + pool *types.ManagedObjectReference, + testTypes ...types.CheckTestType) ([]types.CheckResult, error) { + + req := types.CheckVmConfig_Task{ + This: c.Reference(), + Spec: spec, + Vm: vm, + Host: host, + Pool: pool, + TestType: checkTestTypesToStrings(testTypes), + } + + res, err := methods.CheckVmConfig_Task(ctx, c.c, &req) + if err != nil { + return nil, err + } + + ti, err := NewTask(c.c, res.Returnval).WaitForResult(ctx) + if err != nil { + return nil, err + } + + return ti.Result.(types.ArrayOfCheckResult).CheckResult, nil +} + +func checkTestTypesToStrings(testTypes []types.CheckTestType) []string { + if len(testTypes) == 0 { + return nil + } + + s := make([]string, len(testTypes)) + for i := range testTypes { + s[i] = string(testTypes[i]) + } + return s +} diff --git a/vendor/github.com/vmware/govmomi/object/vm_provisioning_checker.go b/vendor/github.com/vmware/govmomi/object/vm_provisioning_checker.go new file mode 100644 index 00000000..2cab176f --- /dev/null +++ b/vendor/github.com/vmware/govmomi/object/vm_provisioning_checker.go @@ -0,0 +1,67 @@ +/* +Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package object + +import ( + "context" + + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/types" +) + +// VmProvisioningChecker models the ProvisioningChecker, a singleton managed +// object that can answer questions about the feasibility of certain +// provisioning operations. +// +// For more information, see: +// https://dp-downloads.broadcom.com/api-content/apis/API_VWSA_001/8.0U3/html/ReferenceGuides/vim.vm.check.ProvisioningChecker.html +type VmProvisioningChecker struct { + Common +} + +func NewVmProvisioningChecker(c *vim25.Client) *VmProvisioningChecker { + return &VmProvisioningChecker{ + Common: NewCommon(c, *c.ServiceContent.VmProvisioningChecker), + } +} + +func (c VmProvisioningChecker) CheckRelocate( + ctx context.Context, + vm types.ManagedObjectReference, + spec types.VirtualMachineRelocateSpec, + testTypes ...types.CheckTestType) ([]types.CheckResult, error) { + + req := types.CheckRelocate_Task{ + This: c.Reference(), + Vm: vm, + Spec: spec, + TestType: checkTestTypesToStrings(testTypes), + } + + res, err := methods.CheckRelocate_Task(ctx, c.c, &req) + if err != nil { + return nil, err + } + + ti, err := NewTask(c.c, res.Returnval).WaitForResult(ctx) + if err != nil { + return nil, err + } + + return ti.Result.(types.ArrayOfCheckResult).CheckResult, nil +} diff --git a/vendor/github.com/vmware/govmomi/govc/importx/archive.go b/vendor/github.com/vmware/govmomi/ovf/importer/archive.go similarity index 69% rename from vendor/github.com/vmware/govmomi/govc/importx/archive.go rename to vendor/github.com/vmware/govmomi/ovf/importer/archive.go index fef36abd..6b3f631f 100644 --- a/vendor/github.com/vmware/govmomi/govc/importx/archive.go +++ b/vendor/github.com/vmware/govmomi/ovf/importer/archive.go @@ -1,11 +1,11 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -14,14 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package importx +package importer import ( "archive/tar" "bytes" "context" "errors" - "flag" "fmt" "io" "net/url" @@ -31,32 +30,12 @@ import ( "strings" "github.com/vmware/govmomi/ovf" - "github.com/vmware/govmomi/vapi/library" "github.com/vmware/govmomi/vim25" "github.com/vmware/govmomi/vim25/soap" ) -// ArchiveFlag doesn't register any flags; -// only encapsulates some common archive related functionality. -type ArchiveFlag struct { - Archive - - manifest map[string]*library.Checksum -} - -func newArchiveFlag(ctx context.Context) (*ArchiveFlag, context.Context) { - return &ArchiveFlag{}, ctx -} - -func (f *ArchiveFlag) Register(ctx context.Context, fs *flag.FlagSet) { -} - -func (f *ArchiveFlag) Process(ctx context.Context) error { - return nil -} - -func (f *ArchiveFlag) ReadOvf(fpath string) ([]byte, error) { - r, _, err := f.Open(fpath) +func ReadOvf(fpath string, a Archive) ([]byte, error) { + r, _, err := a.Open(fpath) if err != nil { return nil, err } @@ -65,7 +44,7 @@ func (f *ArchiveFlag) ReadOvf(fpath string) ([]byte, error) { return io.ReadAll(r) } -func (f *ArchiveFlag) ReadEnvelope(data []byte) (*ovf.Envelope, error) { +func ReadEnvelope(data []byte) (*ovf.Envelope, error) { e, err := ovf.Unmarshal(bytes.NewReader(data)) if err != nil { return nil, fmt.Errorf("failed to parse ovf: %s", err) @@ -74,22 +53,6 @@ func (f *ArchiveFlag) ReadEnvelope(data []byte) (*ovf.Envelope, error) { return e, nil } -func (f *ArchiveFlag) readManifest(fpath string) error { - base := filepath.Base(fpath) - ext := filepath.Ext(base) - mfName := strings.Replace(base, ext, ".mf", 1) - - mf, _, err := f.Open(mfName) - if err != nil { - msg := fmt.Sprintf("manifest %q: %s", mf, err) - fmt.Fprintln(os.Stderr, msg) - return errors.New(msg) - } - f.manifest, err = library.ReadManifest(mf) - _ = mf.Close() - return err -} - type Archive interface { Open(string) (io.ReadCloser, int64, error) } @@ -163,7 +126,7 @@ type Opener struct { *vim25.Client } -func isRemotePath(path string) bool { +func IsRemotePath(path string) bool { if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") { return true } @@ -185,7 +148,7 @@ func (o Opener) OpenLocal(path string) (io.ReadCloser, int64, error) { } func (o Opener) OpenFile(path string) (io.ReadCloser, int64, error) { - if isRemotePath(path) { + if IsRemotePath(path) { return o.OpenRemote(path) } return o.OpenLocal(path) diff --git a/vendor/github.com/vmware/govmomi/govc/importx/importable.go b/vendor/github.com/vmware/govmomi/ovf/importer/importable.go similarity index 91% rename from vendor/github.com/vmware/govmomi/govc/importx/importable.go rename to vendor/github.com/vmware/govmomi/ovf/importer/importable.go index 14e31670..a43c7fc5 100644 --- a/vendor/github.com/vmware/govmomi/govc/importx/importable.go +++ b/vendor/github.com/vmware/govmomi/ovf/importer/importable.go @@ -1,11 +1,11 @@ /* -Copyright (c) 2014 VMware, Inc. All Rights Reserved. +Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package importx +package importer import ( "fmt" diff --git a/vendor/github.com/vmware/govmomi/ovf/importer/importer.go b/vendor/github.com/vmware/govmomi/ovf/importer/importer.go new file mode 100644 index 00000000..55382f06 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/ovf/importer/importer.go @@ -0,0 +1,326 @@ +/* +Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package importer + +import ( + "bytes" + "context" + "errors" + "fmt" + "os" + "path" + "path/filepath" + "strings" + + "github.com/vmware/govmomi/find" + "github.com/vmware/govmomi/nfc" + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/ovf" + "github.com/vmware/govmomi/vapi/library" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/progress" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type Importer struct { + Log progress.LogFunc + + Name string + VerifyManifest bool + Hidden bool + + Client *vim25.Client + Finder *find.Finder + Sinker progress.Sinker + + Datacenter *object.Datacenter + Datastore *object.Datastore + ResourcePool *object.ResourcePool + Host *object.HostSystem + Folder *object.Folder + + Archive Archive + Manifest map[string]*library.Checksum +} + +func (imp *Importer) ReadManifest(fpath string) error { + base := filepath.Base(fpath) + ext := filepath.Ext(base) + mfName := strings.Replace(base, ext, ".mf", 1) + + mf, _, err := imp.Archive.Open(mfName) + if err != nil { + msg := fmt.Sprintf("manifest %q: %s", mf, err) + fmt.Fprintln(os.Stderr, msg) + return errors.New(msg) + } + imp.Manifest, err = library.ReadManifest(mf) + _ = mf.Close() + return err +} + +func (imp *Importer) Import(ctx context.Context, fpath string, opts Options) (*types.ManagedObjectReference, error) { + + o, err := ReadOvf(fpath, imp.Archive) + if err != nil { + return nil, err + } + + e, err := ReadEnvelope(o) + if err != nil { + return nil, fmt.Errorf("failed to parse ovf: %s", err) + } + + if e.VirtualSystem != nil { + if e.VirtualSystem != nil { + if opts.Name == nil { + opts.Name = &e.VirtualSystem.ID + if e.VirtualSystem.Name != nil { + opts.Name = e.VirtualSystem.Name + } + } + } + if imp.Hidden { + // TODO: userConfigurable is optional and defaults to false, so we should *add* userConfigurable=true + // if not set for a Property. But, there'd be a bunch more work involved to preserve other data in doing + // a complete xml.Marshal of the .ovf + o = bytes.ReplaceAll(o, []byte(`userConfigurable="false"`), []byte(`userConfigurable="true"`)) + } + } + + name := "Govc Virtual Appliance" + if opts.Name != nil { + name = *opts.Name + } + + nmap, err := imp.NetworkMap(ctx, e, opts.NetworkMapping) + if err != nil { + return nil, err + } + + cisp := types.OvfCreateImportSpecParams{ + DiskProvisioning: opts.DiskProvisioning, + EntityName: name, + IpAllocationPolicy: opts.IPAllocationPolicy, + IpProtocol: opts.IPProtocol, + OvfManagerCommonParams: types.OvfManagerCommonParams{ + DeploymentOption: opts.Deployment, + Locale: "US"}, + PropertyMapping: OVFMap(opts.PropertyMapping), + NetworkMapping: nmap, + } + + m := ovf.NewManager(imp.Client) + spec, err := m.CreateImportSpec(ctx, string(o), imp.ResourcePool, imp.Datastore, cisp) + if err != nil { + return nil, err + } + if spec.Error != nil { + return nil, errors.New(spec.Error[0].LocalizedMessage) + } + if spec.Warning != nil { + for _, w := range spec.Warning { + _, _ = imp.Log(fmt.Sprintf("Warning: %s\n", w.LocalizedMessage)) + } + } + + if opts.Annotation != "" { + switch s := spec.ImportSpec.(type) { + case *types.VirtualMachineImportSpec: + s.ConfigSpec.Annotation = opts.Annotation + case *types.VirtualAppImportSpec: + s.VAppConfigSpec.Annotation = opts.Annotation + } + } + + if imp.VerifyManifest { + if err := imp.ReadManifest(fpath); err != nil { + return nil, err + } + } + + lease, err := imp.ResourcePool.ImportVApp(ctx, spec.ImportSpec, imp.Folder, imp.Host) + if err != nil { + return nil, err + } + + info, err := lease.Wait(ctx, spec.FileItem) + if err != nil { + return nil, err + } + + u := lease.StartUpdater(ctx, info) + defer u.Done() + + for _, i := range info.Items { + if err := imp.Upload(ctx, lease, i); err != nil { + return nil, err + } + } + + return &info.Entity, lease.Complete(ctx) +} + +func (imp *Importer) NetworkMap(ctx context.Context, e *ovf.Envelope, networks []Network) ([]types.OvfNetworkMapping, error) { + var nmap []types.OvfNetworkMapping + for _, m := range networks { + if m.Network == "" { + continue // Not set, let vSphere choose the default network + } + if err := ValidateNetwork(e, m); err != nil && imp.Log != nil { + _, _ = imp.Log(err.Error() + "\n") + } + + var ref types.ManagedObjectReference + + net, err := imp.Finder.Network(ctx, m.Network) + if err != nil { + switch err.(type) { + case *find.NotFoundError: + if !ref.FromString(m.Network) { + return nil, err + } // else this is a raw MO ref + default: + return nil, err + } + } else { + ref = net.Reference() + } + + nmap = append(nmap, types.OvfNetworkMapping{ + Name: m.Name, + Network: ref, + }) + } + + return nmap, nil +} + +func OVFMap(op []Property) (p []types.KeyValue) { + for _, v := range op { + p = append(p, types.KeyValue{ + Key: v.Key, + Value: v.Value, + }) + } + + return +} + +func ValidateNetwork(e *ovf.Envelope, net Network) error { + var names []string + + if e.Network != nil { + for _, n := range e.Network.Networks { + if n.Name == net.Name { + return nil + } + names = append(names, n.Name) + } + } + + return fmt.Errorf("warning: invalid NetworkMapping.Name=%q, valid names=%s", net.Name, names) +} + +func ValidateChecksum(ctx context.Context, lease *nfc.Lease, sum *library.Checksum, file string, key string) error { + // Perform the checksum match eagerly, after each file upload, instead + // of after uploading all the files, to provide fail-fast behavior. + // (Trade-off here is multiple GetManifest() API calls to the server.) + manifests, err := lease.GetManifest(ctx) + if err != nil { + return err + } + for _, m := range manifests { + if m.Key == key { + // Compare server-side computed checksum of uploaded file + // against the client's manifest entry (assuming client's + // manifest has correct checksums - client doesn't compute + // checksum of the file before uploading). + + // Try matching sha1 first (newer versions have moved to sha256). + if strings.ToUpper(sum.Algorithm) == "SHA1" { + if sum.Checksum != m.Sha1 { + msg := fmt.Sprintf("manifest checksum %v mismatch with uploaded checksum %v for file %v", + sum.Checksum, m.Sha1, file) + return errors.New(msg) + } + // Uploaded file checksum computed by server matches with local manifest entry. + return nil + } + // If not sha1, check for other types (in a separate field). + if !strings.EqualFold(sum.Algorithm, m.ChecksumType) { + msg := fmt.Sprintf("manifest checksum type %v mismatch with uploaded checksum type %v for file %v", + sum.Algorithm, m.ChecksumType, file) + return errors.New(msg) + } + if !strings.EqualFold(sum.Checksum, m.Checksum) { + msg := fmt.Sprintf("manifest checksum %v mismatch with uploaded checksum %v for file %v", + sum.Checksum, m.Checksum, file) + return errors.New(msg) + } + // Uploaded file checksum computed by server matches with local manifest entry. + return nil + } + } + msg := fmt.Sprintf("missing manifest entry on server for uploaded file %v (key %v), manifests=%#v", file, key, manifests) + return errors.New(msg) +} + +func (imp *Importer) Upload(ctx context.Context, lease *nfc.Lease, item nfc.FileItem) error { + file := item.Path + + f, size, err := imp.Archive.Open(file) + if err != nil { + return err + } + defer f.Close() + + logger := progress.NewProgressLogger(imp.Log, fmt.Sprintf("Uploading %s... ", path.Base(file))) + defer logger.Wait() + + opts := soap.Upload{ + ContentLength: size, + Progress: logger, + } + + err = lease.Upload(ctx, item, f, opts) + if err != nil { + return err + } + + if imp.VerifyManifest { + mapImportKeyToKey := func(urls []types.HttpNfcLeaseDeviceUrl, importKey string) string { + for _, url := range urls { + if url.ImportKey == importKey { + return url.Key + } + } + return "" + } + leaseInfo, err := lease.Wait(ctx, nil) + if err != nil { + return err + } + sum, ok := imp.Manifest[file] + if !ok { + return fmt.Errorf("missing checksum for %v in manifest file", file) + } + return ValidateChecksum(ctx, lease, sum, file, mapImportKeyToKey(leaseInfo.DeviceUrl, item.DeviceId)) + } + return nil +} diff --git a/vendor/github.com/vmware/govmomi/ovf/importer/options.go b/vendor/github.com/vmware/govmomi/ovf/importer/options.go new file mode 100644 index 00000000..cbb00af6 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/ovf/importer/options.go @@ -0,0 +1,91 @@ +/* +Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package importer + +import ( + "encoding/json" + + "github.com/vmware/govmomi/ovf" + "github.com/vmware/govmomi/vim25/types" +) + +type KeyValue struct { + Key string + Value string +} + +// case insensitive for Key + Value +func (kv *KeyValue) UnmarshalJSON(b []byte) error { + e := struct { + types.KeyValue + Key *string + Value *string + }{ + types.KeyValue{}, &kv.Key, &kv.Value, + } + + err := json.Unmarshal(b, &e) + if err != nil { + return err + } + + if kv.Key == "" { + kv.Key = e.KeyValue.Key // "key" + } + + if kv.Value == "" { + kv.Value = e.KeyValue.Value // "value" + } + + return nil +} + +type Property struct { + KeyValue + Spec *ovf.Property `json:",omitempty"` +} + +type Network struct { + Name string + Network string +} + +type Options struct { + AllDeploymentOptions []string `json:",omitempty"` + Deployment string `json:",omitempty"` + + AllDiskProvisioningOptions []string `json:",omitempty"` + DiskProvisioning string + + AllIPAllocationPolicyOptions []string `json:",omitempty"` + IPAllocationPolicy string + + AllIPProtocolOptions []string `json:",omitempty"` + IPProtocol string + + PropertyMapping []Property `json:",omitempty"` + + NetworkMapping []Network `json:",omitempty"` + + Annotation string `json:",omitempty"` + + MarkAsTemplate bool + PowerOn bool + InjectOvfEnv bool + WaitForIP bool + Name *string +} diff --git a/vendor/github.com/vmware/govmomi/ovf/importer/spec.go b/vendor/github.com/vmware/govmomi/ovf/importer/spec.go new file mode 100644 index 00000000..a0028ec7 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/ovf/importer/spec.go @@ -0,0 +1,138 @@ +/* +Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package importer + +import ( + "golang.org/x/text/cases" + "golang.org/x/text/language" + + "github.com/vmware/govmomi/ovf" + "github.com/vmware/govmomi/vim25/types" +) + +var ( + allDiskProvisioningOptions = types.OvfCreateImportSpecParamsDiskProvisioningType("").Strings() + + allIPAllocationPolicyOptions = types.VAppIPAssignmentInfoIpAllocationPolicy("").Strings() + + allIPProtocolOptions = types.VAppIPAssignmentInfoProtocols("").Strings() +) + +func SpecMap(e *ovf.Envelope, hidden, verbose bool) (res []Property) { + if e == nil || e.VirtualSystem == nil { + return nil + } + + for _, p := range e.VirtualSystem.Product { + for i, v := range p.Property { + if v.UserConfigurable == nil { + continue + } + if !*v.UserConfigurable && !hidden { + continue + } + + d := "" + if v.Default != nil { + d = *v.Default + } + + // vSphere only accept True/False as boolean values for some reason + if v.Type == "boolean" { + d = cases.Title(language.Und).String(d) + } + + np := Property{KeyValue: KeyValue{Key: p.Key(v), Value: d}} + + if verbose { + np.Spec = &p.Property[i] + } + + res = append(res, np) + } + } + + return +} + +func Spec(fpath string, a Archive, hidden, verbose bool) (*Options, error) { + e := &ovf.Envelope{} + if fpath != "" { + d, err := ReadOvf(fpath, a) + if err != nil { + return nil, err + } + + if e, err = ReadEnvelope(d); err != nil { + return nil, err + } + } + + var deploymentOptions []string + if e.DeploymentOption != nil && e.DeploymentOption.Configuration != nil { + // add default first + for _, c := range e.DeploymentOption.Configuration { + if c.Default != nil && *c.Default { + deploymentOptions = append(deploymentOptions, c.ID) + } + } + + for _, c := range e.DeploymentOption.Configuration { + if c.Default == nil || !*c.Default { + deploymentOptions = append(deploymentOptions, c.ID) + } + } + } + + o := Options{ + DiskProvisioning: allDiskProvisioningOptions[0], + IPAllocationPolicy: allIPAllocationPolicyOptions[0], + IPProtocol: allIPProtocolOptions[0], + MarkAsTemplate: false, + PowerOn: false, + WaitForIP: false, + InjectOvfEnv: false, + PropertyMapping: SpecMap(e, hidden, verbose), + } + + if deploymentOptions != nil { + o.Deployment = deploymentOptions[0] + } + + if e.VirtualSystem != nil && e.VirtualSystem.Annotation != nil { + for _, a := range e.VirtualSystem.Annotation { + o.Annotation += a.Annotation + } + } + + if e.Network != nil { + for _, net := range e.Network.Networks { + o.NetworkMapping = append(o.NetworkMapping, Network{net.Name, ""}) + } + } + + if verbose { + if deploymentOptions != nil { + o.AllDeploymentOptions = deploymentOptions + } + o.AllDiskProvisioningOptions = allDiskProvisioningOptions + o.AllIPAllocationPolicyOptions = allIPAllocationPolicyOptions + o.AllIPProtocolOptions = allIPProtocolOptions + } + + return &o, nil +} diff --git a/vendor/github.com/vmware/govmomi/vapi/library/finder/path.go b/vendor/github.com/vmware/govmomi/vapi/library/finder/path.go index 213e1ebd..7605627f 100644 --- a/vendor/github.com/vmware/govmomi/vapi/library/finder/path.go +++ b/vendor/github.com/vmware/govmomi/vapi/library/finder/path.go @@ -21,10 +21,13 @@ import ( "fmt" "net/url" "path" + "strings" "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/vapi/library" "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" ) @@ -130,3 +133,50 @@ func (f *PathFinder) datastoreName(ctx context.Context, id string) (string, erro f.cache[id] = name return name, nil } + +// ResolveLibraryItemStorage transforms StorageURIs Datastore url (uuid) to Datastore name. +func (f *PathFinder) ResolveLibraryItemStorage(ctx context.Context, storage []library.Storage) error { + // TODO: + // - reuse PathFinder.cache + // - the transform here isn't Content Library specific, but is currently the only known use case + backing := map[string]*mo.Datastore{} + var ids []types.ManagedObjectReference + + // don't think we can have more than 1 Datastore backing currently, future proof anyhow + for _, item := range storage { + id := item.StorageBacking.DatastoreID + if _, ok := backing[id]; ok { + continue + } + backing[id] = nil + ids = append(ids, types.ManagedObjectReference{Type: "Datastore", Value: id}) + } + + var ds []mo.Datastore + pc := property.DefaultCollector(f.c) + if err := pc.Retrieve(ctx, ids, []string{"name", "info.url"}, &ds); err != nil { + return err + } + + for i := range ds { + backing[ds[i].Self.Value] = &ds[i] + } + + for _, item := range storage { + b := backing[item.StorageBacking.DatastoreID] + dsurl := b.Info.GetDatastoreInfo().Url + + for i := range item.StorageURIs { + u := strings.TrimPrefix(item.StorageURIs[i], dsurl) + u = strings.TrimPrefix(u, "/") + u = strings.SplitN(u, "?", 2)[0] // strip query, if any + + item.StorageURIs[i] = (&object.DatastorePath{ + Datastore: b.Name, + Path: u, + }).String() + } + } + + return nil +} diff --git a/vendor/github.com/vmware/govmomi/vim25/mo/type_info.go b/vendor/github.com/vmware/govmomi/vim25/mo/type_info.go index 21f59291..003b56c2 100644 --- a/vendor/github.com/vmware/govmomi/vim25/mo/type_info.go +++ b/vendor/github.com/vmware/govmomi/vim25/mo/type_info.go @@ -35,9 +35,6 @@ type typeInfo struct { // Map property names to field indices. props map[string][]int - - // Use base type for interface indices. - base bool } var typeInfoLock sync.RWMutex @@ -68,20 +65,22 @@ func typeInfoForType(tname string) *typeInfo { func baseType(ftyp reflect.Type) reflect.Type { base := strings.TrimPrefix(ftyp.Name(), "Base") + switch base { + case "MethodFault": + return nil + } if kind, ok := types.TypeFunc()(base); ok { return kind } - return ftyp + return nil } -func newTypeInfo(typ reflect.Type, base ...bool) *typeInfo { +func newTypeInfo(typ reflect.Type) *typeInfo { t := typeInfo{ typ: typ, props: make(map[string][]int), } - if len(base) == 1 { - t.base = base[0] - } + t.build(typ, "", []int{}) return &t @@ -170,13 +169,16 @@ func (t *typeInfo) build(typ reflect.Type, fn string, fi []int) { t.build(ftyp, fnc, fic) } + // Base type can only access base fields, for example Datastore.Info + // is types.BaseDataStore, so we create a new(types.DatastoreInfo) // Indexed property path may traverse into array element fields. // When interface, use the base type to index fields. // For example, BaseVirtualDevice: // config.hardware.device[4000].deviceInfo.label - if t.base && ftyp.Kind() == reflect.Interface { - base := baseType(ftyp) - t.build(base, fnc, fic) + if ftyp.Kind() == reflect.Interface { + if base := baseType(ftyp); base != nil { + t.build(base, fnc, fic) + } } } } @@ -283,7 +285,7 @@ func assignValue(val reflect.Value, fi []int, pv reflect.Value, field ...string) item = reflect.New(rt.Elem()) } - field := newTypeInfo(item.Type(), true) + field := newTypeInfo(item.Type()) if ix, ok := field.props[path]; ok { assignValue(item, ix, pv) } diff --git a/vendor/github.com/vmware/govmomi/vim25/progress/loger.go b/vendor/github.com/vmware/govmomi/vim25/progress/loger.go new file mode 100644 index 00000000..b1356688 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vim25/progress/loger.go @@ -0,0 +1,125 @@ +/* +Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package progress + +import ( + "fmt" + "io" + "sync" + "time" +) + +type LogFunc func(msg string) (int, error) + +type ProgressLogger struct { + log LogFunc + prefix string + + wg sync.WaitGroup + + sink chan chan Report + done chan struct{} +} + +func NewProgressLogger(log LogFunc, prefix string) *ProgressLogger { + p := &ProgressLogger{ + log: log, + prefix: prefix, + + sink: make(chan chan Report), + done: make(chan struct{}), + } + + p.wg.Add(1) + + go p.loopA() + + return p +} + +// loopA runs before Sink() has been called. +func (p *ProgressLogger) loopA() { + var err error + + defer p.wg.Done() + + tick := time.NewTicker(100 * time.Millisecond) + defer tick.Stop() + + called := false + + for stop := false; !stop; { + select { + case ch := <-p.sink: + err = p.loopB(tick, ch) + stop = true + called = true + case <-p.done: + stop = true + case <-tick.C: + line := fmt.Sprintf("\r%s", p.prefix) + p.log(line) + } + } + + if err != nil && err != io.EOF { + p.log(fmt.Sprintf("\r%sError: %s\n", p.prefix, err)) + } else if called { + p.log(fmt.Sprintf("\r%sOK\n", p.prefix)) + } +} + +// loopA runs after Sink() has been called. +func (p *ProgressLogger) loopB(tick *time.Ticker, ch <-chan Report) error { + var r Report + var ok bool + var err error + + for ok = true; ok; { + select { + case r, ok = <-ch: + if !ok { + break + } + err = r.Error() + case <-tick.C: + line := fmt.Sprintf("\r%s", p.prefix) + if r != nil { + line += fmt.Sprintf("(%.0f%%", r.Percentage()) + detail := r.Detail() + if detail != "" { + line += fmt.Sprintf(", %s", detail) + } + line += ")" + } + p.log(line) + } + } + + return err +} + +func (p *ProgressLogger) Sink() chan<- Report { + ch := make(chan Report) + p.sink <- ch + return ch +} + +func (p *ProgressLogger) Wait() { + close(p.done) + p.wg.Wait() +} diff --git a/vendor/github.com/vmware/govmomi/vim25/types/types.go b/vendor/github.com/vmware/govmomi/vim25/types/types.go index 0f8dab11..43f8c0a9 100644 --- a/vendor/github.com/vmware/govmomi/vim25/types/types.go +++ b/vendor/github.com/vmware/govmomi/vim25/types/types.go @@ -91361,7 +91361,7 @@ type VirtualMachineVirtualNuma struct { // vNUMA node. // If set to be non zero, VM uses the value as vNUMA node size. // If unset, the VM continue to follow the behavior in last poweron. - CoresPerNumaNode int32 `xml:"coresPerNumaNode,omitempty" json:"coresPerNumaNode,omitempty"` + CoresPerNumaNode *int32 `xml:"coresPerNumaNode" json:"coresPerNumaNode,omitempty"` // Capability to expose virtual NUMA when CPU hotadd is enabled. // // If set to true, ESXi will consider exposing virtual NUMA to @@ -91389,7 +91389,7 @@ type VirtualMachineVirtualNumaInfo struct { // field should be ignored. // In other cases, this field represents the virtual NUMA node size // seen by the guest. - CoresPerNumaNode int32 `xml:"coresPerNumaNode,omitempty" json:"coresPerNumaNode,omitempty"` + CoresPerNumaNode *int32 `xml:"coresPerNumaNode" json:"coresPerNumaNode,omitempty"` // Whether coresPerNode is determined automatically. AutoCoresPerNumaNode *bool `xml:"autoCoresPerNumaNode" json:"autoCoresPerNumaNode,omitempty"` // Whether virtual NUMA topology is exposed when CPU hotadd is diff --git a/vendor/github.com/vmware/govmomi/vmdk/disk_info.go b/vendor/github.com/vmware/govmomi/vmdk/disk_info.go new file mode 100644 index 00000000..6c919f10 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vmdk/disk_info.go @@ -0,0 +1,164 @@ +/* +Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vmdk + +import ( + "context" + "fmt" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +type VirtualDiskInfo struct { + CapacityInBytes int64 + DeviceKey int32 + FileName string + Size int64 + UniqueSize int64 +} + +// GetVirtualDiskInfoByUUID returns information about a virtual disk identified +// by the provided UUID. This method is valid for the following backing types: +// +// - VirtualDiskFlatVer2BackingInfo +// - VirtualDiskSeSparseBackingInfo +// - VirtualDiskRawDiskMappingVer1BackingInfo +// - VirtualDiskSparseVer2BackingInfo +// - VirtualDiskRawDiskVer2BackingInfo +// +// These are the only backing types that have a UUID property for comparing the +// provided value. +func GetVirtualDiskInfoByUUID( + ctx context.Context, + client *vim25.Client, + mo mo.VirtualMachine, + fetchProperties bool, + diskUUID string) (VirtualDiskInfo, error) { + + if diskUUID == "" { + return VirtualDiskInfo{}, fmt.Errorf("diskUUID is empty") + } + + switch { + case fetchProperties, + mo.Config == nil, + mo.Config.Hardware.Device == nil, + mo.LayoutEx == nil, + mo.LayoutEx.Disk == nil, + mo.LayoutEx.File == nil: + + if ctx == nil { + return VirtualDiskInfo{}, fmt.Errorf("ctx is nil") + } + if client == nil { + return VirtualDiskInfo{}, fmt.Errorf("client is nil") + } + + obj := object.NewVirtualMachine(client, mo.Self) + + if err := obj.Properties( + ctx, + mo.Self, + []string{"config", "layoutEx"}, + &mo); err != nil { + + return VirtualDiskInfo{}, + fmt.Errorf("failed to retrieve properties: %w", err) + } + } + + // Find the disk by UUID by inspecting all of the disk backing types that + // can have an associated UUID. + var ( + disk *types.VirtualDisk + fileName string + ) + for i := range mo.Config.Hardware.Device { + switch tvd := mo.Config.Hardware.Device[i].(type) { + case *types.VirtualDisk: + switch tb := tvd.Backing.(type) { + case *types.VirtualDiskFlatVer2BackingInfo: + if tb.Uuid == diskUUID { + disk = tvd + fileName = tb.FileName + } + case *types.VirtualDiskSeSparseBackingInfo: + if tb.Uuid == diskUUID { + disk = tvd + fileName = tb.FileName + } + case *types.VirtualDiskRawDiskMappingVer1BackingInfo: + if tb.Uuid == diskUUID { + disk = tvd + fileName = tb.FileName + } + case *types.VirtualDiskSparseVer2BackingInfo: + if tb.Uuid == diskUUID { + disk = tvd + fileName = tb.FileName + } + case *types.VirtualDiskRawDiskVer2BackingInfo: + if tb.Uuid == diskUUID { + disk = tvd + fileName = tb.DescriptorFileName + } + } + } + } + + if disk == nil { + return VirtualDiskInfo{}, + fmt.Errorf("disk not found with uuid %q", diskUUID) + } + + // Build a lookup table for determining if file key belongs to this disk + // chain. + diskFileKeys := map[int32]struct{}{} + for i := range mo.LayoutEx.Disk { + if d := mo.LayoutEx.Disk[i]; d.Key == disk.Key { + for j := range d.Chain { + for k := range d.Chain[j].FileKey { + diskFileKeys[d.Chain[j].FileKey[k]] = struct{}{} + } + } + } + } + + // Sum the disk's total size and unique size. + var ( + size int64 + uniqueSize int64 + ) + for i := range mo.LayoutEx.File { + f := mo.LayoutEx.File[i] + if _, ok := diskFileKeys[f.Key]; ok { + size += f.Size + uniqueSize += f.UniqueSize + } + } + + return VirtualDiskInfo{ + CapacityInBytes: disk.CapacityInBytes, + DeviceKey: disk.Key, + FileName: fileName, + Size: size, + UniqueSize: uniqueSize, + }, nil +} diff --git a/vendor/github.com/vmware/govmomi/vmdk/import.go b/vendor/github.com/vmware/govmomi/vmdk/import.go index 652c008b..9354a553 100644 --- a/vendor/github.com/vmware/govmomi/vmdk/import.go +++ b/vendor/github.com/vmware/govmomi/vmdk/import.go @@ -10,7 +10,7 @@ You may obtain a copy of the License at Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -nSee the License for the specific language governing permissions and +See the License for the specific language governing permissions and limitations under the License. */ @@ -41,8 +41,8 @@ var ( ErrInvalidFormat = errors.New("vmdk: invalid format (must be streamOptimized)") ) -// info is used to inspect a vmdk and generate an ovf template -type info struct { +// Info is used to inspect a vmdk and generate an ovf template +type Info struct { Header struct { MagicNumber uint32 Version uint32 @@ -56,15 +56,15 @@ type info struct { ImportName string } -// stat looks at the vmdk header to make sure the format is streamOptimized and +// Stat looks at the vmdk header to make sure the format is streamOptimized and // extracts the disk capacity required to properly generate the ovf descriptor. -func stat(name string) (*info, error) { +func Stat(name string) (*Info, error) { f, err := os.Open(filepath.Clean(name)) if err != nil { return nil, err } - var di info + var di Info var buf bytes.Buffer @@ -174,8 +174,8 @@ var ovfenv = ` ` -// ovf returns an expanded descriptor template -func (di *info) ovf() (string, error) { +// OVF returns an expanded descriptor template +func (di *Info) OVF() (string, error) { var buf bytes.Buffer tmpl, err := template.New("ovf").Parse(ovfenv) @@ -209,7 +209,7 @@ func Import(ctx context.Context, c *vim25.Client, name string, datastore *object m := ovf.NewManager(c) fm := datastore.NewFileManager(p.Datacenter, p.Force) - disk, err := stat(name) + disk, err := Stat(name) if err != nil { return err } @@ -245,7 +245,7 @@ func Import(ctx context.Context, c *vim25.Client, name string, datastore *object } // Expand the ovf template - descriptor, err := disk.ovf() + descriptor, err := disk.OVF() if err != nil { return err } diff --git a/vendor/modules.txt b/vendor/modules.txt index 889b5e68..9d9baf68 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -60,7 +60,7 @@ github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function github.com/google/go-cmp/cmp/internal/value -# github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da +# github.com/google/pprof v0.0.0-20240722153945-304e4f0156b8 ## explicit; go 1.19 github.com/google/pprof/profile # github.com/google/subcommands v1.2.0 @@ -198,8 +198,8 @@ github.com/pkg/errors # github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde ## explicit github.com/tidwall/transform -# github.com/vmware/govmomi v0.38.0 -## explicit; go 1.19 +# github.com/vmware/govmomi v0.39.0 +## explicit; go 1.21 github.com/vmware/govmomi github.com/vmware/govmomi/cns github.com/vmware/govmomi/cns/methods @@ -226,6 +226,7 @@ github.com/vmware/govmomi/list github.com/vmware/govmomi/nfc github.com/vmware/govmomi/object github.com/vmware/govmomi/ovf +github.com/vmware/govmomi/ovf/importer github.com/vmware/govmomi/pbm github.com/vmware/govmomi/pbm/methods github.com/vmware/govmomi/pbm/types