Adding template feature
This commit is contained in:
parent
dc2124aa2a
commit
c8747f138f
|
@ -8,6 +8,8 @@ import (
|
||||||
"github.com/hashicorp/packer/helper/config"
|
"github.com/hashicorp/packer/helper/config"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
type Config struct {
|
type Config struct {
|
||||||
common.PackerConfig `mapstructure:",squash"`
|
common.PackerConfig `mapstructure:",squash"`
|
||||||
|
@ -53,6 +55,11 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := p.configureURL(); err != nil {
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
if len(errs.Errors) > 0 {
|
if len(errs.Errors) > 0 {
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
@ -60,7 +67,48 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
|
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
|
||||||
|
state := new(multistep.BasicStateBag)
|
||||||
|
state.Put("ui", ui)
|
||||||
|
|
||||||
|
//FIXME I've trash environment, so I need to wait :(
|
||||||
|
ui.Message("Waiting 10s for VMWare vSphere to start")
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
|
||||||
|
steps := []multistep.Step{
|
||||||
|
&StepVSphereClient{
|
||||||
|
Datacenter: p.config.Datacenter,
|
||||||
|
VMName: p.config.VMName,
|
||||||
|
Url: p.url,
|
||||||
|
},
|
||||||
|
&StepFetchVm{
|
||||||
|
VMName: p.config.VMName,
|
||||||
|
},
|
||||||
|
&StepCreateFolder{
|
||||||
|
Folder: p.config.Folder,
|
||||||
|
},
|
||||||
|
&StepMarkAsTemplate{},
|
||||||
|
&StepMoveTemplate{
|
||||||
|
Folder: p.config.Folder,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
runner := &multistep.BasicRunner{Steps: steps}
|
||||||
|
runner.Run(state)
|
||||||
|
|
||||||
|
if rawErr, ok := state.GetOk("error"); ok {
|
||||||
|
return artifact, true, rawErr.(error)
|
||||||
|
}
|
||||||
return artifact, true, nil
|
return artifact, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PostProcessor) configureURL() error {
|
||||||
|
sdk, err := url.Parse("https://" + p.config.Host + "/sdk")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sdk.User = url.UserPassword(p.config.Username, p.config.Password)
|
||||||
|
p.url = sdk
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package vsphere_tpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/vmware/govmomi/find"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepCreateFolder struct {
|
||||||
|
Folder string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCreateFolder) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
ctx := state.Get("context").(context.Context)
|
||||||
|
f := state.Get("finder").(*find.Finder)
|
||||||
|
d := state.Get("datacenter").(string)
|
||||||
|
|
||||||
|
if s.Folder != "" {
|
||||||
|
ui.Say("Creating or checking destination folders...")
|
||||||
|
|
||||||
|
if !strings.HasPrefix(s.Folder, "/") {
|
||||||
|
s.Folder = filepath.Join("/", s.Folder)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := s.Folder
|
||||||
|
base := filepath.Join("/", d, "vm")
|
||||||
|
var folders []string
|
||||||
|
var folder, root string
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, err := f.Folder(ctx, filepath.ToSlash(filepath.Join(base, path)))
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
root, folder = filepath.Split(path)
|
||||||
|
folders = append(folders, folder)
|
||||||
|
if i := strings.LastIndex(path, "/"); i == 0 {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
path = path[:i]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := len(folders) - 1; i >= 0; i-- {
|
||||||
|
folder, err := f.Folder(ctx, filepath.ToSlash(filepath.Join(base, "/", root)))
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Message(fmt.Sprintf("Creating folder: %v", folders[i]))
|
||||||
|
|
||||||
|
if _, err = folder.CreateFolder(ctx, folders[i]); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
root = filepath.Join(root, folders[i], "/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCreateFolder) Cleanup(multistep.StateBag) {}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package vsphere_tpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/vmware/govmomi/find"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepFetchVm struct {
|
||||||
|
VMName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepFetchVm) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
ctx := state.Get("context").(context.Context)
|
||||||
|
f := state.Get("finder").(*find.Finder)
|
||||||
|
|
||||||
|
ui.Say("Fetching VM...")
|
||||||
|
|
||||||
|
vm, err := f.VirtualMachine(ctx, s.VMName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Put("vm", vm)
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepFetchVm) Cleanup(multistep.StateBag) {}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package vsphere_tpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/vmware/govmomi/object"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepMarkAsTemplate struct {
|
||||||
|
Folder string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
ctx := state.Get("context").(context.Context)
|
||||||
|
vm := state.Get("vm").(*object.VirtualMachine)
|
||||||
|
|
||||||
|
ui.Say("Marking as a template...")
|
||||||
|
|
||||||
|
if err := vm.MarkAsTemplate(ctx); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepMarkAsTemplate) Cleanup(multistep.StateBag) {}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
package vsphere_tpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/vmware/govmomi/find"
|
||||||
|
"github.com/vmware/govmomi/object"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepMoveTemplate struct {
|
||||||
|
Folder string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepMoveTemplate) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
ctx := state.Get("context").(context.Context)
|
||||||
|
finder := state.Get("finder").(*find.Finder)
|
||||||
|
vm := state.Get("vm").(*object.VirtualMachine)
|
||||||
|
d := state.Get("datacenter").(string)
|
||||||
|
|
||||||
|
if s.Folder != "" {
|
||||||
|
ui.Say("Moving template...")
|
||||||
|
|
||||||
|
folder, err := finder.Folder(ctx, fmt.Sprintf("/%v/vm/%v", d, s.Folder))
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
task, err := folder.MoveInto(ctx, []types.ManagedObjectReference{vm.Reference()})
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
if err = task.Wait(ctx); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepMoveTemplate) Cleanup(multistep.StateBag) {}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package vsphere_tpl
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/vmware/govmomi"
|
||||||
|
"github.com/vmware/govmomi/find"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepVSphereClient struct {
|
||||||
|
Url *url.URL
|
||||||
|
Datacenter string
|
||||||
|
VMName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepVSphereClient) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
ctx := context.Background()
|
||||||
|
cli, err := govmomi.NewClient(ctx, s.Url, true)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
finder := find.NewFinder(cli.Client, false)
|
||||||
|
datacenter, err := finder.DatacenterOrDefault(ctx, s.Datacenter)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
finder.SetDatacenter(datacenter)
|
||||||
|
|
||||||
|
state.Put("datacenter", datacenter.Name())
|
||||||
|
state.Put("finder", finder)
|
||||||
|
state.Put("context", ctx)
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepVSphereClient) Cleanup(multistep.StateBag) {}
|
|
@ -0,0 +1,263 @@
|
||||||
|
# changelog
|
||||||
|
|
||||||
|
### 0.15.0 (2017-06-19)
|
||||||
|
|
||||||
|
* WaitOptions.MaxWaitSeconds is now optional
|
||||||
|
|
||||||
|
* Support removal of ExtraConfig entries
|
||||||
|
|
||||||
|
* GuestPosixFileAttributes OwnerId and GroupId fields are now pointers,
|
||||||
|
rather than omitempty ints to allow chown with root uid:gid
|
||||||
|
|
||||||
|
* Updated examples/ using view package
|
||||||
|
|
||||||
|
* Add DatastoreFile.TailFunc method
|
||||||
|
|
||||||
|
* Export VirtualMachine.FindSnapshot method
|
||||||
|
|
||||||
|
* Add AuthorizationManager {Enable,Disable}Methods
|
||||||
|
|
||||||
|
* Add PBM client
|
||||||
|
|
||||||
|
### 0.14.0 (2017-04-08)
|
||||||
|
|
||||||
|
* Add view.ContainerView type and methods
|
||||||
|
|
||||||
|
* Add Collector.RetrieveWithFilter method
|
||||||
|
|
||||||
|
* Add property.Filter type
|
||||||
|
|
||||||
|
* Implement EthernetCardBackingInfo for OpaqueNetwork
|
||||||
|
|
||||||
|
* Finder: support changing object root in find mode
|
||||||
|
|
||||||
|
* Add VirtualDiskManager.QueryVirtualDiskInfo
|
||||||
|
|
||||||
|
* Add performance.Manager APIs
|
||||||
|
|
||||||
|
### 0.13.0 (2017-03-02)
|
||||||
|
|
||||||
|
* Add DatastoreFileManager API wrapper
|
||||||
|
|
||||||
|
* Add HostVsanInternalSystem API wrappers
|
||||||
|
|
||||||
|
* Add Container support to view package
|
||||||
|
|
||||||
|
* Finder supports Folder recursion without specifying a path
|
||||||
|
|
||||||
|
* Add VirtualMachine.QueryConfigTarget method
|
||||||
|
|
||||||
|
* Add device option to VirtualMachine.WaitForNetIP
|
||||||
|
|
||||||
|
* Remove _Task suffix from vapp methods
|
||||||
|
|
||||||
|
### 0.12.1 (2016-12-19)
|
||||||
|
|
||||||
|
* Add DiagnosticLog helper
|
||||||
|
|
||||||
|
* Add DatastorePath helper
|
||||||
|
|
||||||
|
### 0.12.0 (2016-12-01)
|
||||||
|
|
||||||
|
* Disable use of service ticket for datastore HTTP access by default
|
||||||
|
|
||||||
|
* Attach context to HTTP requests for cancellations
|
||||||
|
|
||||||
|
* Update to vim25/6.5 API
|
||||||
|
|
||||||
|
### 0.11.4 (2016-11-15)
|
||||||
|
|
||||||
|
* Add object.AuthorizationManager methods: RetrieveRolePermissions, RetrieveAllPermissions, AddRole, RemoveRole, UpdateRole
|
||||||
|
|
||||||
|
### 0.11.3 (2016-11-08)
|
||||||
|
|
||||||
|
* Allow DatastoreFile.Follow reader to drain current body after stopping
|
||||||
|
|
||||||
|
### 0.11.2 (2016-11-01)
|
||||||
|
|
||||||
|
* Avoid possible NPE in VirtualMachine.Device method
|
||||||
|
|
||||||
|
* Add support for OpaqueNetwork type to Finder
|
||||||
|
|
||||||
|
* Add HostConfigManager.AccountManager support for ESX 5.5
|
||||||
|
|
||||||
|
### 0.11.1 (2016-10-27)
|
||||||
|
|
||||||
|
* Add Finder.ResourcePoolListAll method
|
||||||
|
|
||||||
|
### 0.11.0 (2016-10-25)
|
||||||
|
|
||||||
|
* Add object.DistributedVirtualPortgroup.Reconfigure method
|
||||||
|
|
||||||
|
### 0.10.0 (2016-10-20)
|
||||||
|
|
||||||
|
* Add option to set soap.Client.UserAgent
|
||||||
|
|
||||||
|
* Add service ticket thumbprint validation
|
||||||
|
|
||||||
|
* Update use of http.DefaultTransport fields to 1.7
|
||||||
|
|
||||||
|
* Set default locale to en_US (override with GOVMOMI_LOCALE env var)
|
||||||
|
|
||||||
|
* Add object.HostCertificateInfo (types.HostCertificateManagerCertificateInfo helpers)
|
||||||
|
|
||||||
|
* Add object.HostCertificateManager type and HostConfigManager.CertificateManager method
|
||||||
|
|
||||||
|
* Add soap.Client SetRootCAs and SetDialTLS methods
|
||||||
|
|
||||||
|
### 0.9.0 (2016-09-09)
|
||||||
|
|
||||||
|
* Add object.DatastoreFile helpers for streaming and tailing datastore files
|
||||||
|
|
||||||
|
* Add object VirtualMachine.Unregister method
|
||||||
|
|
||||||
|
* Add object.ListView methods: Add, Remove, Reset
|
||||||
|
|
||||||
|
* Update to Go 1.7 - using stdlib's context package
|
||||||
|
|
||||||
|
### 0.8.0 (2016-06-30)
|
||||||
|
|
||||||
|
* Add session.Manager.AcquireLocalTicket
|
||||||
|
|
||||||
|
* Include StoragePod in Finder.FolderList
|
||||||
|
|
||||||
|
* Add Finder methods for finding by ManagedObjectReference: Element, ObjectReference
|
||||||
|
|
||||||
|
* Add mo.ManagedObjectReference methods: Reference, String, FromString
|
||||||
|
|
||||||
|
* Add support using SessionManagerGenericServiceTicket.HostName for Datastore HTTP access
|
||||||
|
|
||||||
|
### 0.7.1 (2016-06-03)
|
||||||
|
|
||||||
|
* Fix object.ObjectName method
|
||||||
|
|
||||||
|
### 0.7.0 (2016-06-02)
|
||||||
|
|
||||||
|
* Move InventoryPath field to object.Common
|
||||||
|
|
||||||
|
* Add HostDatastoreSystem.CreateLocalDatastore method
|
||||||
|
|
||||||
|
* Add DatastoreNamespaceManager methods: CreateDirectory, DeleteDirectory
|
||||||
|
|
||||||
|
* Add HostServiceSystem
|
||||||
|
|
||||||
|
* Add HostStorageSystem methods: MarkAsSdd, MarkAsNonSdd, MarkAsLocal, MarkAsNonLocal
|
||||||
|
|
||||||
|
* Add HostStorageSystem.RescanAllHba method
|
||||||
|
|
||||||
|
### 0.6.2 (2016-05-11)
|
||||||
|
|
||||||
|
* Get complete file details in Datastore.Stat
|
||||||
|
|
||||||
|
* SOAP decoding fixes
|
||||||
|
|
||||||
|
* Add VirtualMachine.RemoveAllSnapshot
|
||||||
|
|
||||||
|
### 0.6.1 (2016-04-30)
|
||||||
|
|
||||||
|
* Fix mo.Entity interface
|
||||||
|
|
||||||
|
### 0.6.0 (2016-04-29)
|
||||||
|
|
||||||
|
* Add Common.Rename method
|
||||||
|
|
||||||
|
* Add mo.Entity interface
|
||||||
|
|
||||||
|
* Add OptionManager
|
||||||
|
|
||||||
|
* Add Finder.FolderList method
|
||||||
|
|
||||||
|
* Add VirtualMachine.WaitForNetIP method
|
||||||
|
|
||||||
|
* Add VirtualMachine.RevertToSnapshot method
|
||||||
|
|
||||||
|
* Add Datastore.Download method
|
||||||
|
|
||||||
|
### 0.5.0 (2016-03-30)
|
||||||
|
|
||||||
|
Generated fields using xsd type 'int' change to Go type 'int32'
|
||||||
|
|
||||||
|
VirtualDevice.UnitNumber field changed to pointer type
|
||||||
|
|
||||||
|
### 0.4.0 (2016-02-26)
|
||||||
|
|
||||||
|
* Add method to convert virtual device list to array with virtual device
|
||||||
|
changes that can be used in the VirtualMachineConfigSpec.
|
||||||
|
|
||||||
|
* Make datastore cluster traversable in lister
|
||||||
|
|
||||||
|
* Add finder.DatastoreCluster methods (also known as storage pods)
|
||||||
|
|
||||||
|
* Add Drone CI check
|
||||||
|
|
||||||
|
* Add object.Datastore Type and AttachedClusterHosts methods
|
||||||
|
|
||||||
|
* Add finder.*OrDefault methods
|
||||||
|
|
||||||
|
### 0.3.0 (2016-01-16)
|
||||||
|
|
||||||
|
* Add object.VirtualNicManager wrapper
|
||||||
|
|
||||||
|
* Add object.HostVsanSystem wrapper
|
||||||
|
|
||||||
|
* Add object.HostSystem methods: EnterMaintenanceMode, ExitMaintenanceMode, Disconnect, Reconnect
|
||||||
|
|
||||||
|
* Add finder.Folder method
|
||||||
|
|
||||||
|
* Add object.Common.Destroy method
|
||||||
|
|
||||||
|
* Add object.ComputeResource.Reconfigure method
|
||||||
|
|
||||||
|
* Add license.AssignmentManager wrapper
|
||||||
|
|
||||||
|
* Add object.HostFirewallSystem wrapper
|
||||||
|
|
||||||
|
* Add object.DiagnosticManager wrapper
|
||||||
|
|
||||||
|
* Add LoginExtensionByCertificate support
|
||||||
|
|
||||||
|
* Add object.ExtensionManager
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
### 0.2.0 (2015-09-15)
|
||||||
|
|
||||||
|
* Update to vim25/6.0 API
|
||||||
|
|
||||||
|
* Stop returning children from `ManagedObjectList`
|
||||||
|
|
||||||
|
Change the `ManagedObjectList` function in the `find` package to only
|
||||||
|
return the managed objects specified by the path argument and not their
|
||||||
|
children. The original behavior was used by govc's `ls` command and is
|
||||||
|
now available in the newly added function `ManagedObjectListChildren`.
|
||||||
|
|
||||||
|
* Add retry functionality to vim25 package
|
||||||
|
|
||||||
|
* Change finder functions to no longer take varargs
|
||||||
|
|
||||||
|
The `find` package had functions to return a list of objects, given a
|
||||||
|
variable number of patterns. This makes it impossible to distinguish which
|
||||||
|
patterns produced results and which ones didn't.
|
||||||
|
|
||||||
|
In particular for govc, where multiple arguments can be passed from the
|
||||||
|
command line, it is useful to let the user know which ones produce results
|
||||||
|
and which ones don't.
|
||||||
|
|
||||||
|
To evaluate multiple patterns, the user should call the find functions
|
||||||
|
multiple times (either serially or in parallel).
|
||||||
|
|
||||||
|
* Make optional boolean fields pointers (`vim25/types`).
|
||||||
|
|
||||||
|
False is the zero value of a boolean field, which means they are not serialized
|
||||||
|
if the field is marked "omitempty". If the field is a pointer instead, the zero
|
||||||
|
value will be the nil pointer, and both true and false values are serialized.
|
||||||
|
|
||||||
|
### 0.1.0 (2015-03-17)
|
||||||
|
|
||||||
|
Prior to this version the API of this library was in flux.
|
||||||
|
|
||||||
|
Notable changes w.r.t. the state of this library before March 2015 are:
|
||||||
|
|
||||||
|
* All functions that may execute a request take a `context.Context` parameter.
|
||||||
|
* The `vim25` package contains a minimal client implementation.
|
||||||
|
* The property collector and its convenience functions live in the `property` package.
|
|
@ -0,0 +1,101 @@
|
||||||
|
# Contributing to govmomi
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
First, fork the repository on GitHub to your personal account.
|
||||||
|
|
||||||
|
Note that _GOPATH_ can be any directory, the example below uses _$HOME/govmomi_.
|
||||||
|
Change _$USER_ below to your github username if they are not the same.
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
export GOPATH=$HOME/govmomi
|
||||||
|
go get github.com/vmware/govmomi
|
||||||
|
cd $GOPATH/src/github.com/vmware/govmomi
|
||||||
|
git config push.default nothing # anything to avoid pushing to vmware/govmomi by default
|
||||||
|
git remote rename origin vmware
|
||||||
|
git remote add $USER git@github.com:$USER/govmomi.git
|
||||||
|
git fetch $USER
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installing from source
|
||||||
|
|
||||||
|
Compile the govmomi libraries and install govc using:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
go install -v github.com/vmware/govmomi/govc
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that **govc/build.sh** is only used for building release binaries.
|
||||||
|
|
||||||
|
## Contribution flow
|
||||||
|
|
||||||
|
This is a rough outline of what a contributor's workflow looks like:
|
||||||
|
|
||||||
|
- Create a topic branch from where you want to base your work.
|
||||||
|
- Make commits of logical units.
|
||||||
|
- Make sure your commit messages are in the proper format (see below).
|
||||||
|
- Update CHANGELOG.md and/or govc/CHANGELOG.md when appropriate.
|
||||||
|
- Push your changes to a topic branch in your fork of the repository.
|
||||||
|
- Submit a pull request to vmware/govmomi.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
git checkout -b my-new-feature vmware/master
|
||||||
|
git commit -a
|
||||||
|
git push $USER my-new-feature
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stay in sync with upstream
|
||||||
|
|
||||||
|
When your branch gets out of sync with the vmware/master branch, use the following to update:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
git checkout my-new-feature
|
||||||
|
git fetch -a
|
||||||
|
git rebase vmware/master
|
||||||
|
git push --force-with-lease $USER my-new-feature
|
||||||
|
```
|
||||||
|
|
||||||
|
### Updating pull requests
|
||||||
|
|
||||||
|
If your PR fails to pass CI or needs changes based on code review, you'll most likely want to squash these changes into
|
||||||
|
existing commits.
|
||||||
|
|
||||||
|
If your pull request contains a single commit or your changes are related to the most recent commit, you can simply
|
||||||
|
amend the commit.
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
git add .
|
||||||
|
git commit --amend
|
||||||
|
git push --force-with-lease $USER my-new-feature
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to squash changes into an earlier commit, you can use:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
git add .
|
||||||
|
git commit --fixup <commit>
|
||||||
|
git rebase -i --autosquash vmware/master
|
||||||
|
git push --force-with-lease $USER my-new-feature
|
||||||
|
```
|
||||||
|
|
||||||
|
Be sure to add a comment to the PR indicating your new changes are ready to review, as github does not generate a
|
||||||
|
notification when you git push.
|
||||||
|
|
||||||
|
### Code style
|
||||||
|
|
||||||
|
The coding style suggested by the Golang community is used in govmomi. See the
|
||||||
|
[style doc](https://github.com/golang/go/wiki/CodeReviewComments) for details.
|
||||||
|
|
||||||
|
Try to limit column width to 120 characters for both code and markdown documents such as this one.
|
||||||
|
|
||||||
|
### Format of the Commit Message
|
||||||
|
|
||||||
|
We follow the conventions on [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/).
|
||||||
|
|
||||||
|
Be sure to include any related GitHub issue references in the commit message.
|
||||||
|
|
||||||
|
## Reporting Bugs and Creating Issues
|
||||||
|
|
||||||
|
When opening a new issue, try to roughly follow the commit message format conventions above.
|
|
@ -0,0 +1,61 @@
|
||||||
|
# People who can (and typically have) contributed to this repository.
|
||||||
|
#
|
||||||
|
# This script is generated by contributors.sh
|
||||||
|
#
|
||||||
|
|
||||||
|
abrarshivani <abrarshivani@users.noreply.github.com>
|
||||||
|
Alvaro Miranda <kikitux@gmail.com>
|
||||||
|
amandahla <amanda.andrade@serpro.gov.br>
|
||||||
|
Amit Bathla <abathla@.vmware.com>
|
||||||
|
Andrew Chin <andrew@andrewtchin.com>
|
||||||
|
aniketGslab <aniket.shinde@gslab.com>
|
||||||
|
Arran Walker <arran.walker@zopa.com>
|
||||||
|
Aryeh Weinreb <aryehweinreb@gmail.com>
|
||||||
|
Austin Parker <aparker@apprenda.com>
|
||||||
|
Balu Dontu <bdontu@vmware.com>
|
||||||
|
bastienbc <bastien.barbe.creuly@gmail.com>
|
||||||
|
Bob Killen <killen.bob@gmail.com>
|
||||||
|
Brad Fitzpatrick <bradfitz@golang.org>
|
||||||
|
Bruce Downs <bruceadowns@gmail.com>
|
||||||
|
Cédric Blomart <cblomart@gmail.com>
|
||||||
|
Christian Höltje <docwhat@gerf.org>
|
||||||
|
Clint Greenwood <cgreenwood@vmware.com>
|
||||||
|
Danny Lockard <danny.lockard@banno.com>
|
||||||
|
Dave Tucker <dave@dtucker.co.uk>
|
||||||
|
Davide Agnello <dagnello@hp.com>
|
||||||
|
David Stark <dave@davidstark.name>
|
||||||
|
Doug MacEachern <dougm@vmware.com>
|
||||||
|
Eloy Coto <eloy.coto@gmail.com>
|
||||||
|
Eric Gray <egray@vmware.com>
|
||||||
|
Eric Yutao <eric.yutao@gmail.com>
|
||||||
|
Fabio Rapposelli <fabio@vmware.com>
|
||||||
|
Faiyaz Ahmed <ahmedf@vmware.com>
|
||||||
|
forkbomber <forkbomber@users.noreply.github.com>
|
||||||
|
Gavin Gray <gavin@infinio.com>
|
||||||
|
Gavrie Philipson <gavrie.philipson@elastifile.com>
|
||||||
|
George Hicken <ghicken@vmware.com>
|
||||||
|
Gerrit Renker <Gerrit.Renker@ctl.io>
|
||||||
|
gthombare <gthombare@vmware.com>
|
||||||
|
Hasan Mahmood <mahmoodh@vmware.com>
|
||||||
|
Henrik Hodne <henrik@travis-ci.com>
|
||||||
|
Isaac Rodman <isaac@eyz.us>
|
||||||
|
Ivan Porto Carrero <icarrero@vmware.com>
|
||||||
|
Jason Kincl <jkincl@gmail.com>
|
||||||
|
Jeremy Canady <jcanady@jackhenry.com>
|
||||||
|
Louie Jiang <jiangl@vmware.com>
|
||||||
|
Marc Carmier <mcarmier@gmail.com>
|
||||||
|
Mevan Samaratunga <mevansam@gmail.com>
|
||||||
|
Nicolas Lamirault <nicolas.lamirault@gmail.com>
|
||||||
|
Pieter Noordhuis <pnoordhuis@vmware.com>
|
||||||
|
runner.mei <runner.mei@gmail.com>
|
||||||
|
S.Çağlar Onur <conur@vmware.com>
|
||||||
|
Sergey Ignatov <sergey.ignatov@jetbrains.com>
|
||||||
|
Steve Purcell <steve@sanityinc.com>
|
||||||
|
Takaaki Furukawa <takaaki.frkw@gmail.com>
|
||||||
|
Ted Zlatanov <tzz@lifelogs.com>
|
||||||
|
Thibaut Ackermann <thibaut.ackermann@alcatel-lucent.com>
|
||||||
|
Vadim Egorov <vegorov@vmware.com>
|
||||||
|
Yang Yang <yangy@vmware.com>
|
||||||
|
Yuya Kusakabe <yuya.kusakabe@gmail.com>
|
||||||
|
Zach Tucker <ztucker@vmware.com>
|
||||||
|
Zee Yang <zeey@vmware.com>
|
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,24 @@
|
||||||
|
.PHONY: test
|
||||||
|
|
||||||
|
all: check test
|
||||||
|
|
||||||
|
check: goimports govet
|
||||||
|
|
||||||
|
goimports:
|
||||||
|
@echo checking go imports...
|
||||||
|
@go get golang.org/x/tools/cmd/goimports
|
||||||
|
@! goimports -d . 2>&1 | egrep -v '^$$'
|
||||||
|
|
||||||
|
govet:
|
||||||
|
@echo checking go vet...
|
||||||
|
@go tool vet -structtags=false -methods=false $$(find . -mindepth 1 -maxdepth 1 -type d -not -name vendor)
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test -v $(TEST_OPTS) ./...
|
||||||
|
|
||||||
|
install:
|
||||||
|
go install -v github.com/vmware/govmomi/govc
|
||||||
|
go install -v github.com/vmware/govmomi/vcsim
|
||||||
|
|
||||||
|
doc: install
|
||||||
|
./govc/usage.sh > ./govc/USAGE.md
|
|
@ -0,0 +1,78 @@
|
||||||
|
[![Build Status](https://travis-ci.org/vmware/govmomi.png?branch=master)](https://travis-ci.org/vmware/govmomi)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/vmware/govmomi)](https://goreportcard.com/report/github.com/vmware/govmomi)
|
||||||
|
|
||||||
|
# govmomi
|
||||||
|
|
||||||
|
A Go library for interacting with VMware vSphere APIs (ESXi and/or vCenter).
|
||||||
|
|
||||||
|
For `govc`, a CLI built on top of govmomi, check out the [govc](./govc) directory and [USAGE](./govc/USAGE.md) document.
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
This library is built for and tested against ESXi and vCenter 5.5, 6.0 and 6.5.
|
||||||
|
|
||||||
|
If you're able to use it against older versions of ESXi and/or vCenter, please
|
||||||
|
leave a note and we'll include it in this compatibility list.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
The APIs exposed by this library very closely follow the API described in the [VMware vSphere API Reference Documentation][apiref].
|
||||||
|
Refer to this document to become familiar with the upstream API.
|
||||||
|
|
||||||
|
The code in the `govmomi` package is a wrapper for the code that is generated from the vSphere API description.
|
||||||
|
It primarily provides convenience functions for working with the vSphere API.
|
||||||
|
See [godoc.org][godoc] for documentation.
|
||||||
|
|
||||||
|
[apiref]:http://pubs.vmware.com/vsphere-6-5/index.jsp#com.vmware.wssdk.apiref.doc/right-pane.html
|
||||||
|
[godoc]:http://godoc.org/github.com/vmware/govmomi
|
||||||
|
[drone]:https://drone.io
|
||||||
|
[dronesrc]:https://github.com/drone/drone
|
||||||
|
[dronecli]:http://readme.drone.io/devs/cli/
|
||||||
|
|
||||||
|
#### Building with CI
|
||||||
|
Merges to this repository will trigger builds in both Travis and [Drone][drone].
|
||||||
|
|
||||||
|
To build locally with Drone:
|
||||||
|
- Ensure that you have Docker 1.6 or higher installed.
|
||||||
|
- Install the [Drone command line tools][dronecli].
|
||||||
|
- Run `drone exec` from within the root directory of the govmomi repository.
|
||||||
|
|
||||||
|
## Discussion
|
||||||
|
|
||||||
|
Contributors and users are encouraged to collaborate using GitHub issues and/or
|
||||||
|
[Slack](https://vmwarecode.slack.com/messages/govmomi).
|
||||||
|
Access to Slack requires a [VMware {code} membership](https://code.vmware.com/join/).
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Changes to the API are subject to [semantic versioning](http://semver.org).
|
||||||
|
|
||||||
|
Refer to the [CHANGELOG](CHANGELOG.md) for version to version changes.
|
||||||
|
|
||||||
|
## Projects using govmomi
|
||||||
|
|
||||||
|
* [Docker Machine](https://github.com/docker/machine/tree/master/drivers/vmwarevsphere)
|
||||||
|
|
||||||
|
* [Kubernetes](https://github.com/kubernetes/kubernetes/tree/master/pkg/cloudprovider/providers/vsphere)
|
||||||
|
|
||||||
|
* [Terraform](https://github.com/hashicorp/terraform/tree/master/builtin/providers/vsphere)
|
||||||
|
|
||||||
|
* [VMware VIC Engine](https://github.com/vmware/vic)
|
||||||
|
|
||||||
|
* [Travis CI](https://github.com/travis-ci/jupiter-brain)
|
||||||
|
|
||||||
|
* [collectd-vsphere](https://github.com/travis-ci/collectd-vsphere)
|
||||||
|
|
||||||
|
* [Gru](https://github.com/dnaeon/gru)
|
||||||
|
|
||||||
|
* [Libretto](https://github.com/apcera/libretto/tree/master/virtualmachine/vsphere)
|
||||||
|
|
||||||
|
## Related projects
|
||||||
|
|
||||||
|
* [rbvmomi](https://github.com/vmware/rbvmomi)
|
||||||
|
|
||||||
|
* [pyvmomi](https://github.com/vmware/pyvmomi)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
govmomi is available under the [Apache 2 license](LICENSE).
|
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014-2016 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This package is the root package of the govmomi library.
|
||||||
|
|
||||||
|
The library is structured as follows:
|
||||||
|
|
||||||
|
Package vim25
|
||||||
|
|
||||||
|
The minimal usable functionality is available through the vim25 package.
|
||||||
|
It contains subpackages that contain generated types, managed objects, and all
|
||||||
|
available methods. The vim25 package is entirely independent of the other
|
||||||
|
packages in the govmomi tree -- it has no dependencies on its peers.
|
||||||
|
|
||||||
|
The vim25 package itself contains a client structure that is
|
||||||
|
passed around throughout the entire library. It abstracts a session and its
|
||||||
|
immutable state. See the vim25 package for more information.
|
||||||
|
|
||||||
|
Package session
|
||||||
|
|
||||||
|
The session package contains an abstraction for the session manager that allows
|
||||||
|
a user to login and logout. It also provides access to the current session
|
||||||
|
(i.e. to determine if the user is in fact logged in)
|
||||||
|
|
||||||
|
Package object
|
||||||
|
|
||||||
|
The object package contains wrappers for a selection of managed objects. The
|
||||||
|
constructors of these objects all take a *vim25.Client, which they pass along
|
||||||
|
to derived objects, if applicable.
|
||||||
|
|
||||||
|
Package govc
|
||||||
|
|
||||||
|
The govc package contains the govc CLI. The code in this tree is not intended
|
||||||
|
to be used as a library. Any functionality that govc contains that _could_ be
|
||||||
|
used as a library function but isn't, _should_ live in a root level package.
|
||||||
|
|
||||||
|
Other packages
|
||||||
|
|
||||||
|
Other packages, such as "event", "guest", or "license", provide wrappers for
|
||||||
|
the respective subsystems. They are typically not needed in normal workflows so
|
||||||
|
are kept outside the object package.
|
||||||
|
*/
|
||||||
|
package govmomi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/property"
|
||||||
|
"github.com/vmware/govmomi/session"
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
*vim25.Client
|
||||||
|
|
||||||
|
SessionManager *session.Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient creates a new client from a URL. The client authenticates with the
|
||||||
|
// server with username/password before returning if the URL contains user information.
|
||||||
|
func NewClient(ctx context.Context, u *url.URL, insecure bool) (*Client, error) {
|
||||||
|
soapClient := soap.NewClient(u, insecure)
|
||||||
|
vimClient, err := vim25.NewClient(ctx, soapClient)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &Client{
|
||||||
|
Client: vimClient,
|
||||||
|
SessionManager: session.NewManager(vimClient),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only login if the URL contains user information.
|
||||||
|
if u.User != nil {
|
||||||
|
err = c.Login(ctx, u.User)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientWithCertificate creates a new client from a URL. The client authenticates with the
|
||||||
|
// server with the certificate before returning if the URL contains user information.
|
||||||
|
func NewClientWithCertificate(ctx context.Context, u *url.URL, insecure bool, cert tls.Certificate) (*Client, error) {
|
||||||
|
soapClient := soap.NewClient(u, insecure)
|
||||||
|
soapClient.SetCertificate(cert)
|
||||||
|
vimClient, err := vim25.NewClient(ctx, soapClient)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &Client{
|
||||||
|
Client: vimClient,
|
||||||
|
SessionManager: session.NewManager(vimClient),
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.User != nil {
|
||||||
|
err = c.LoginExtensionByCertificate(ctx, u.User.Username(), "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login dispatches to the SessionManager.
|
||||||
|
func (c *Client) Login(ctx context.Context, u *url.Userinfo) error {
|
||||||
|
return c.SessionManager.Login(ctx, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login dispatches to the SessionManager.
|
||||||
|
func (c *Client) LoginExtensionByCertificate(ctx context.Context, key string, locale string) error {
|
||||||
|
return c.SessionManager.LoginExtensionByCertificate(ctx, key, locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logout dispatches to the SessionManager.
|
||||||
|
func (c *Client) Logout(ctx context.Context) error {
|
||||||
|
// Close any idle connections after logging out.
|
||||||
|
defer c.Client.CloseIdleConnections()
|
||||||
|
return c.SessionManager.Logout(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropertyCollector returns the session's default property collector.
|
||||||
|
func (c *Client) PropertyCollector() *property.Collector {
|
||||||
|
return property.DefaultCollector(c.Client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveOne dispatches to the Retrieve function on the default property collector.
|
||||||
|
func (c *Client) RetrieveOne(ctx context.Context, obj types.ManagedObjectReference, p []string, dst interface{}) error {
|
||||||
|
return c.PropertyCollector().RetrieveOne(ctx, obj, p, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve dispatches to the Retrieve function on the default property collector.
|
||||||
|
func (c *Client) Retrieve(ctx context.Context, objs []types.ManagedObjectReference, p []string, dst interface{}) error {
|
||||||
|
return c.PropertyCollector().Retrieve(ctx, objs, p, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait dispatches to property.Wait.
|
||||||
|
func (c *Client) Wait(ctx context.Context, obj types.ManagedObjectReference, ps []string, f func([]types.PropertyChange) bool) error {
|
||||||
|
return property.Wait(ctx, c.PropertyCollector(), obj, ps, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsVC returns true if we are connected to a vCenter
|
||||||
|
func (c *Client) IsVC() bool {
|
||||||
|
return c.Client.IsVC()
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014-2017 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 find implements inventory listing and searching.
|
||||||
|
|
||||||
|
The Finder is an alternative to the object.SearchIndex FindByInventoryPath() and FindChild() methods.
|
||||||
|
SearchIndex.FindByInventoryPath requires an absolute path, whereas the Finder also supports relative paths
|
||||||
|
and patterns via path.Match.
|
||||||
|
SearchIndex.FindChild requires a parent to find the child, whereas the Finder also supports an ancestor via
|
||||||
|
recursive object traversal.
|
||||||
|
|
||||||
|
The various Finder methods accept a "path" argument, which can absolute or relative to the Folder for the object type.
|
||||||
|
The Finder supports two modes, "list" and "find". The "list" mode behaves like the "ls" command, only searching within
|
||||||
|
the immediate path. The "find" mode behaves like the "find" command, with the search starting at the immediate path but
|
||||||
|
also recursing into sub Folders relative to the Datacenter. The default mode is "list" if the given path contains a "/",
|
||||||
|
otherwise "find" mode is used.
|
||||||
|
|
||||||
|
The exception is to use a "..." wildcard with a path to find all objects recursively underneath any root object.
|
||||||
|
For example: VirtualMachineList("/DC1/...")
|
||||||
|
|
||||||
|
See also: https://github.com/vmware/govmomi/blob/master/govc/README.md#usage
|
||||||
|
*/
|
||||||
|
package find
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 find
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type NotFoundError struct {
|
||||||
|
kind string
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *NotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("%s '%s' not found", e.kind, e.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MultipleFoundError struct {
|
||||||
|
kind string
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MultipleFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("path '%s' resolves to multiple %ss", e.path, e.kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultNotFoundError struct {
|
||||||
|
kind string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DefaultNotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("no default %s found", e.kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultMultipleFoundError struct {
|
||||||
|
kind string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DefaultMultipleFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("default %s resolves to multiple instances, please specify", e.kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toDefaultError(err error) error {
|
||||||
|
switch e := err.(type) {
|
||||||
|
case *NotFoundError:
|
||||||
|
return &DefaultNotFoundError{e.kind}
|
||||||
|
case *MultipleFoundError:
|
||||||
|
return &DefaultMultipleFoundError{e.kind}
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014-2017 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 find
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/list"
|
||||||
|
"github.com/vmware/govmomi/object"
|
||||||
|
"github.com/vmware/govmomi/property"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
)
|
||||||
|
|
||||||
|
// spec is used to specify per-search configuration, independent of the Finder instance.
|
||||||
|
type spec struct {
|
||||||
|
// Relative returns the root object to resolve Relative paths (starts with ".")
|
||||||
|
Relative func(ctx context.Context) (object.Reference, error)
|
||||||
|
|
||||||
|
// ListMode can be used to optionally force "ls" behavior, rather than "find" behavior
|
||||||
|
ListMode *bool
|
||||||
|
|
||||||
|
// Contents configures the Recurser to list the Contents of traversable leaf nodes.
|
||||||
|
// This is typically set to true when used from the ls command, where listing
|
||||||
|
// a folder means listing its Contents. This is typically set to false for
|
||||||
|
// commands that take managed entities that are not folders as input.
|
||||||
|
Contents bool
|
||||||
|
|
||||||
|
// Parents specifies the types which can contain the child types being searched for.
|
||||||
|
// for example, when searching for a HostSystem, parent types can be
|
||||||
|
// "ComputeResource" or "ClusterComputeResource".
|
||||||
|
Parents []string
|
||||||
|
|
||||||
|
// Include specifies which types to be included in the results, used only in "find" mode.
|
||||||
|
Include []string
|
||||||
|
|
||||||
|
// Nested should be set to types that can be Nested, used only in "find" mode.
|
||||||
|
Nested []string
|
||||||
|
|
||||||
|
// ChildType avoids traversing into folders that can't contain the Include types, used only in "find" mode.
|
||||||
|
ChildType []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *spec) traversable(o mo.Reference) bool {
|
||||||
|
ref := o.Reference()
|
||||||
|
|
||||||
|
switch ref.Type {
|
||||||
|
case "Datacenter":
|
||||||
|
if len(s.Include) == 1 && s.Include[0] == "Datacenter" {
|
||||||
|
// No point in traversing deeper as Datacenters cannot be nested
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case "Folder":
|
||||||
|
if f, ok := o.(mo.Folder); ok {
|
||||||
|
// TODO: Not making use of this yet, but here we can optimize when searching the entire
|
||||||
|
// inventory across Datacenters for specific types, for example: 'govc ls -t VirtualMachine /**'
|
||||||
|
// should not traverse into a Datacenter's host, network or datatore folders.
|
||||||
|
if !s.traversableChildType(f.ChildType) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, kind := range s.Parents {
|
||||||
|
if kind == ref.Type {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *spec) traversableChildType(ctypes []string) bool {
|
||||||
|
if len(s.ChildType) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range ctypes {
|
||||||
|
for _, c := range s.ChildType {
|
||||||
|
if t == c {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *spec) wanted(e list.Element) bool {
|
||||||
|
if len(s.Include) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
w := e.Object.Reference().Type
|
||||||
|
|
||||||
|
for _, kind := range s.Include {
|
||||||
|
if w == kind {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// listMode is a global option to revert to the original Finder behavior,
|
||||||
|
// disabling the newer "find" mode.
|
||||||
|
var listMode = os.Getenv("GOVMOMI_FINDER_LIST_MODE") == "true"
|
||||||
|
|
||||||
|
func (s *spec) listMode(isPath bool) bool {
|
||||||
|
if listMode {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.ListMode != nil {
|
||||||
|
return *s.ListMode
|
||||||
|
}
|
||||||
|
|
||||||
|
return isPath
|
||||||
|
}
|
||||||
|
|
||||||
|
type recurser struct {
|
||||||
|
Collector *property.Collector
|
||||||
|
|
||||||
|
// All configures the recurses to fetch complete objects for leaf nodes.
|
||||||
|
All bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r recurser) List(ctx context.Context, s *spec, root list.Element, parts []string) ([]list.Element, error) {
|
||||||
|
if len(parts) == 0 {
|
||||||
|
// Include non-traversable leaf elements in result. For example, consider
|
||||||
|
// the pattern "./vm/my-vm-*", where the pattern should match the VMs and
|
||||||
|
// not try to traverse them.
|
||||||
|
//
|
||||||
|
// Include traversable leaf elements in result, if the contents
|
||||||
|
// field is set to false.
|
||||||
|
//
|
||||||
|
if !s.Contents || !s.traversable(root.Object.Reference()) {
|
||||||
|
return []list.Element{root}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
k := list.Lister{
|
||||||
|
Collector: r.Collector,
|
||||||
|
Reference: root.Object.Reference(),
|
||||||
|
Prefix: root.Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.All && len(parts) < 2 {
|
||||||
|
k.All = true
|
||||||
|
}
|
||||||
|
|
||||||
|
in, err := k.List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This folder is a leaf as far as the glob goes.
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return in, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
all := parts
|
||||||
|
pattern := parts[0]
|
||||||
|
parts = parts[1:]
|
||||||
|
|
||||||
|
var out []list.Element
|
||||||
|
for _, e := range in {
|
||||||
|
matched, err := path.Match(pattern, path.Base(e.Path))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matched {
|
||||||
|
matched = strings.HasSuffix(e.Path, path.Join(all...))
|
||||||
|
if matched {
|
||||||
|
// name contains a '/'
|
||||||
|
out = append(out, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nres, err := r.List(ctx, s, e, parts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, nres...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r recurser) Find(ctx context.Context, s *spec, root list.Element, parts []string) ([]list.Element, error) {
|
||||||
|
var out []list.Element
|
||||||
|
|
||||||
|
if len(parts) > 0 {
|
||||||
|
pattern := parts[0]
|
||||||
|
matched, err := path.Match(pattern, path.Base(root.Path))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if matched && s.wanted(root) {
|
||||||
|
out = append(out, root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.traversable(root.Object) {
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
k := list.Lister{
|
||||||
|
Collector: r.Collector,
|
||||||
|
Reference: root.Object.Reference(),
|
||||||
|
Prefix: root.Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
in, err := k.List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range in {
|
||||||
|
nres, err := r.Find(ctx, s, e, parts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, nres...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
|
@ -0,0 +1,563 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014-2016 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 list
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/property"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Element struct {
|
||||||
|
Path string
|
||||||
|
Object mo.Reference
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Element) String() string {
|
||||||
|
return fmt.Sprintf("%s @ %s", e.Object.Reference(), e.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToElement(r mo.Reference, prefix string) Element {
|
||||||
|
var name string
|
||||||
|
|
||||||
|
// Comments about types to be expected in folders copied from the
|
||||||
|
// documentation of the Folder managed object:
|
||||||
|
// http://pubs.vmware.com/vsphere-55/topic/com.vmware.wssdk.apiref.doc/vim.Folder.html
|
||||||
|
switch m := r.(type) {
|
||||||
|
case mo.Folder:
|
||||||
|
name = m.Name
|
||||||
|
case mo.StoragePod:
|
||||||
|
name = m.Name
|
||||||
|
|
||||||
|
// { "vim.Datacenter" } - Identifies the root folder and its descendant
|
||||||
|
// folders. Data center folders can contain child data center folders and
|
||||||
|
// Datacenter managed objects. Datacenter objects contain virtual machine,
|
||||||
|
// compute resource, network entity, and datastore folders.
|
||||||
|
case mo.Datacenter:
|
||||||
|
name = m.Name
|
||||||
|
|
||||||
|
// { "vim.Virtualmachine", "vim.VirtualApp" } - Identifies a virtual machine
|
||||||
|
// folder. A virtual machine folder may contain child virtual machine
|
||||||
|
// folders. It also can contain VirtualMachine managed objects, templates,
|
||||||
|
// and VirtualApp managed objects.
|
||||||
|
case mo.VirtualMachine:
|
||||||
|
name = m.Name
|
||||||
|
case mo.VirtualApp:
|
||||||
|
name = m.Name
|
||||||
|
|
||||||
|
// { "vim.ComputeResource" } - Identifies a compute resource
|
||||||
|
// folder, which contains child compute resource folders and ComputeResource
|
||||||
|
// hierarchies.
|
||||||
|
case mo.ComputeResource:
|
||||||
|
name = m.Name
|
||||||
|
case mo.ClusterComputeResource:
|
||||||
|
name = m.Name
|
||||||
|
case mo.HostSystem:
|
||||||
|
name = m.Name
|
||||||
|
case mo.ResourcePool:
|
||||||
|
name = m.Name
|
||||||
|
|
||||||
|
// { "vim.Network" } - Identifies a network entity folder.
|
||||||
|
// Network entity folders on a vCenter Server can contain Network,
|
||||||
|
// DistributedVirtualSwitch, and DistributedVirtualPortgroup managed objects.
|
||||||
|
// Network entity folders on an ESXi host can contain only Network objects.
|
||||||
|
case mo.Network:
|
||||||
|
name = m.Name
|
||||||
|
case mo.OpaqueNetwork:
|
||||||
|
name = m.Name
|
||||||
|
case mo.DistributedVirtualSwitch:
|
||||||
|
name = m.Name
|
||||||
|
case mo.DistributedVirtualPortgroup:
|
||||||
|
name = m.Name
|
||||||
|
case mo.VmwareDistributedVirtualSwitch:
|
||||||
|
name = m.Name
|
||||||
|
|
||||||
|
// { "vim.Datastore" } - Identifies a datastore folder. Datastore folders can
|
||||||
|
// contain child datastore folders and Datastore managed objects.
|
||||||
|
case mo.Datastore:
|
||||||
|
name = m.Name
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("not implemented for type " + reflect.TypeOf(r).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
e := Element{
|
||||||
|
Path: path.Join(prefix, name),
|
||||||
|
Object: r,
|
||||||
|
}
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
type Lister struct {
|
||||||
|
Collector *property.Collector
|
||||||
|
Reference types.ManagedObjectReference
|
||||||
|
Prefix string
|
||||||
|
All bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Lister) retrieveProperties(ctx context.Context, req types.RetrieveProperties, dst *[]interface{}) error {
|
||||||
|
res, err := l.Collector.RetrieveProperties(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instead of using mo.LoadRetrievePropertiesResponse, use a custom loop to
|
||||||
|
// iterate over the results and ignore entries that have properties that
|
||||||
|
// could not be retrieved (a non-empty `missingSet` property). Since the
|
||||||
|
// returned objects are enumerated by vSphere in the first place, any object
|
||||||
|
// that has a non-empty `missingSet` property is indicative of a race
|
||||||
|
// condition in vSphere where the object was enumerated initially, but was
|
||||||
|
// removed before its properties could be collected.
|
||||||
|
for _, p := range res.Returnval {
|
||||||
|
v, err := mo.ObjectContentToType(p)
|
||||||
|
if err != nil {
|
||||||
|
// Ignore fault if it is ManagedObjectNotFound
|
||||||
|
if soap.IsVimFault(err) {
|
||||||
|
switch soap.ToVimFault(err).(type) {
|
||||||
|
case *types.ManagedObjectNotFound:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = append(*dst, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Lister) List(ctx context.Context) ([]Element, error) {
|
||||||
|
switch l.Reference.Type {
|
||||||
|
case "Folder", "StoragePod":
|
||||||
|
return l.ListFolder(ctx)
|
||||||
|
case "Datacenter":
|
||||||
|
return l.ListDatacenter(ctx)
|
||||||
|
case "ComputeResource", "ClusterComputeResource":
|
||||||
|
// Treat ComputeResource and ClusterComputeResource as one and the same.
|
||||||
|
// It doesn't matter from the perspective of the lister.
|
||||||
|
return l.ListComputeResource(ctx)
|
||||||
|
case "ResourcePool":
|
||||||
|
return l.ListResourcePool(ctx)
|
||||||
|
case "HostSystem":
|
||||||
|
return l.ListHostSystem(ctx)
|
||||||
|
case "VirtualApp":
|
||||||
|
return l.ListVirtualApp(ctx)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("cannot traverse type " + l.Reference.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Lister) ListFolder(ctx context.Context) ([]Element, error) {
|
||||||
|
spec := types.PropertyFilterSpec{
|
||||||
|
ObjectSet: []types.ObjectSpec{
|
||||||
|
{
|
||||||
|
Obj: l.Reference,
|
||||||
|
SelectSet: []types.BaseSelectionSpec{
|
||||||
|
&types.TraversalSpec{
|
||||||
|
Path: "childEntity",
|
||||||
|
Skip: types.NewBool(false),
|
||||||
|
Type: "Folder",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Skip: types.NewBool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve all objects that we can deal with
|
||||||
|
childTypes := []string{
|
||||||
|
"Folder",
|
||||||
|
"Datacenter",
|
||||||
|
"VirtualApp",
|
||||||
|
"VirtualMachine",
|
||||||
|
"Network",
|
||||||
|
"ComputeResource",
|
||||||
|
"ClusterComputeResource",
|
||||||
|
"Datastore",
|
||||||
|
"DistributedVirtualSwitch",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range childTypes {
|
||||||
|
pspec := types.PropertySpec{
|
||||||
|
Type: t,
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.All {
|
||||||
|
pspec.All = types.NewBool(true)
|
||||||
|
} else {
|
||||||
|
pspec.PathSet = []string{"name"}
|
||||||
|
|
||||||
|
// Additional basic properties.
|
||||||
|
switch t {
|
||||||
|
case "Folder":
|
||||||
|
pspec.PathSet = append(pspec.PathSet, "childType")
|
||||||
|
case "ComputeResource", "ClusterComputeResource":
|
||||||
|
// The ComputeResource and ClusterComputeResource are dereferenced in
|
||||||
|
// the ResourcePoolFlag. Make sure they always have their resourcePool
|
||||||
|
// field populated.
|
||||||
|
pspec.PathSet = append(pspec.PathSet, "resourcePool")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spec.PropSet = append(spec.PropSet, pspec)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.RetrieveProperties{
|
||||||
|
SpecSet: []types.PropertyFilterSpec{spec},
|
||||||
|
}
|
||||||
|
|
||||||
|
var dst []interface{}
|
||||||
|
|
||||||
|
err := l.retrieveProperties(ctx, req, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
es := []Element{}
|
||||||
|
for _, v := range dst {
|
||||||
|
es = append(es, ToElement(v.(mo.Reference), l.Prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
return es, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Lister) ListDatacenter(ctx context.Context) ([]Element, error) {
|
||||||
|
ospec := types.ObjectSpec{
|
||||||
|
Obj: l.Reference,
|
||||||
|
Skip: types.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include every datastore folder in the select set
|
||||||
|
fields := []string{
|
||||||
|
"vmFolder",
|
||||||
|
"hostFolder",
|
||||||
|
"datastoreFolder",
|
||||||
|
"networkFolder",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range fields {
|
||||||
|
tspec := types.TraversalSpec{
|
||||||
|
Path: f,
|
||||||
|
Skip: types.NewBool(false),
|
||||||
|
Type: "Datacenter",
|
||||||
|
}
|
||||||
|
|
||||||
|
ospec.SelectSet = append(ospec.SelectSet, &tspec)
|
||||||
|
}
|
||||||
|
|
||||||
|
pspec := types.PropertySpec{
|
||||||
|
Type: "Folder",
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.All {
|
||||||
|
pspec.All = types.NewBool(true)
|
||||||
|
} else {
|
||||||
|
pspec.PathSet = []string{"name", "childType"}
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.RetrieveProperties{
|
||||||
|
SpecSet: []types.PropertyFilterSpec{
|
||||||
|
{
|
||||||
|
ObjectSet: []types.ObjectSpec{ospec},
|
||||||
|
PropSet: []types.PropertySpec{pspec},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var dst []interface{}
|
||||||
|
|
||||||
|
err := l.retrieveProperties(ctx, req, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
es := []Element{}
|
||||||
|
for _, v := range dst {
|
||||||
|
es = append(es, ToElement(v.(mo.Reference), l.Prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
return es, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Lister) ListComputeResource(ctx context.Context) ([]Element, error) {
|
||||||
|
ospec := types.ObjectSpec{
|
||||||
|
Obj: l.Reference,
|
||||||
|
Skip: types.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := []string{
|
||||||
|
"host",
|
||||||
|
"resourcePool",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range fields {
|
||||||
|
tspec := types.TraversalSpec{
|
||||||
|
Path: f,
|
||||||
|
Skip: types.NewBool(false),
|
||||||
|
Type: "ComputeResource",
|
||||||
|
}
|
||||||
|
|
||||||
|
ospec.SelectSet = append(ospec.SelectSet, &tspec)
|
||||||
|
}
|
||||||
|
|
||||||
|
childTypes := []string{
|
||||||
|
"HostSystem",
|
||||||
|
"ResourcePool",
|
||||||
|
}
|
||||||
|
|
||||||
|
var pspecs []types.PropertySpec
|
||||||
|
for _, t := range childTypes {
|
||||||
|
pspec := types.PropertySpec{
|
||||||
|
Type: t,
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.All {
|
||||||
|
pspec.All = types.NewBool(true)
|
||||||
|
} else {
|
||||||
|
pspec.PathSet = []string{"name"}
|
||||||
|
}
|
||||||
|
|
||||||
|
pspecs = append(pspecs, pspec)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.RetrieveProperties{
|
||||||
|
SpecSet: []types.PropertyFilterSpec{
|
||||||
|
{
|
||||||
|
ObjectSet: []types.ObjectSpec{ospec},
|
||||||
|
PropSet: pspecs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var dst []interface{}
|
||||||
|
|
||||||
|
err := l.retrieveProperties(ctx, req, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
es := []Element{}
|
||||||
|
for _, v := range dst {
|
||||||
|
es = append(es, ToElement(v.(mo.Reference), l.Prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
return es, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Lister) ListResourcePool(ctx context.Context) ([]Element, error) {
|
||||||
|
ospec := types.ObjectSpec{
|
||||||
|
Obj: l.Reference,
|
||||||
|
Skip: types.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := []string{
|
||||||
|
"resourcePool",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range fields {
|
||||||
|
tspec := types.TraversalSpec{
|
||||||
|
Path: f,
|
||||||
|
Skip: types.NewBool(false),
|
||||||
|
Type: "ResourcePool",
|
||||||
|
}
|
||||||
|
|
||||||
|
ospec.SelectSet = append(ospec.SelectSet, &tspec)
|
||||||
|
}
|
||||||
|
|
||||||
|
childTypes := []string{
|
||||||
|
"ResourcePool",
|
||||||
|
}
|
||||||
|
|
||||||
|
var pspecs []types.PropertySpec
|
||||||
|
for _, t := range childTypes {
|
||||||
|
pspec := types.PropertySpec{
|
||||||
|
Type: t,
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.All {
|
||||||
|
pspec.All = types.NewBool(true)
|
||||||
|
} else {
|
||||||
|
pspec.PathSet = []string{"name"}
|
||||||
|
}
|
||||||
|
|
||||||
|
pspecs = append(pspecs, pspec)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.RetrieveProperties{
|
||||||
|
SpecSet: []types.PropertyFilterSpec{
|
||||||
|
{
|
||||||
|
ObjectSet: []types.ObjectSpec{ospec},
|
||||||
|
PropSet: pspecs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var dst []interface{}
|
||||||
|
|
||||||
|
err := l.retrieveProperties(ctx, req, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
es := []Element{}
|
||||||
|
for _, v := range dst {
|
||||||
|
es = append(es, ToElement(v.(mo.Reference), l.Prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
return es, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Lister) ListHostSystem(ctx context.Context) ([]Element, error) {
|
||||||
|
ospec := types.ObjectSpec{
|
||||||
|
Obj: l.Reference,
|
||||||
|
Skip: types.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := []string{
|
||||||
|
"datastore",
|
||||||
|
"network",
|
||||||
|
"vm",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range fields {
|
||||||
|
tspec := types.TraversalSpec{
|
||||||
|
Path: f,
|
||||||
|
Skip: types.NewBool(false),
|
||||||
|
Type: "HostSystem",
|
||||||
|
}
|
||||||
|
|
||||||
|
ospec.SelectSet = append(ospec.SelectSet, &tspec)
|
||||||
|
}
|
||||||
|
|
||||||
|
childTypes := []string{
|
||||||
|
"Datastore",
|
||||||
|
"Network",
|
||||||
|
"VirtualMachine",
|
||||||
|
}
|
||||||
|
|
||||||
|
var pspecs []types.PropertySpec
|
||||||
|
for _, t := range childTypes {
|
||||||
|
pspec := types.PropertySpec{
|
||||||
|
Type: t,
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.All {
|
||||||
|
pspec.All = types.NewBool(true)
|
||||||
|
} else {
|
||||||
|
pspec.PathSet = []string{"name"}
|
||||||
|
}
|
||||||
|
|
||||||
|
pspecs = append(pspecs, pspec)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.RetrieveProperties{
|
||||||
|
SpecSet: []types.PropertyFilterSpec{
|
||||||
|
{
|
||||||
|
ObjectSet: []types.ObjectSpec{ospec},
|
||||||
|
PropSet: pspecs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var dst []interface{}
|
||||||
|
|
||||||
|
err := l.retrieveProperties(ctx, req, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
es := []Element{}
|
||||||
|
for _, v := range dst {
|
||||||
|
es = append(es, ToElement(v.(mo.Reference), l.Prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
return es, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Lister) ListVirtualApp(ctx context.Context) ([]Element, error) {
|
||||||
|
ospec := types.ObjectSpec{
|
||||||
|
Obj: l.Reference,
|
||||||
|
Skip: types.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := []string{
|
||||||
|
"resourcePool",
|
||||||
|
"vm",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range fields {
|
||||||
|
tspec := types.TraversalSpec{
|
||||||
|
Path: f,
|
||||||
|
Skip: types.NewBool(false),
|
||||||
|
Type: "VirtualApp",
|
||||||
|
}
|
||||||
|
|
||||||
|
ospec.SelectSet = append(ospec.SelectSet, &tspec)
|
||||||
|
}
|
||||||
|
|
||||||
|
childTypes := []string{
|
||||||
|
"ResourcePool",
|
||||||
|
"VirtualMachine",
|
||||||
|
}
|
||||||
|
|
||||||
|
var pspecs []types.PropertySpec
|
||||||
|
for _, t := range childTypes {
|
||||||
|
pspec := types.PropertySpec{
|
||||||
|
Type: t,
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.All {
|
||||||
|
pspec.All = types.NewBool(true)
|
||||||
|
} else {
|
||||||
|
pspec.PathSet = []string{"name"}
|
||||||
|
}
|
||||||
|
|
||||||
|
pspecs = append(pspecs, pspec)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.RetrieveProperties{
|
||||||
|
SpecSet: []types.PropertyFilterSpec{
|
||||||
|
{
|
||||||
|
ObjectSet: []types.ObjectSpec{ospec},
|
||||||
|
PropSet: pspecs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var dst []interface{}
|
||||||
|
|
||||||
|
err := l.retrieveProperties(ctx, req, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
es := []Element{}
|
||||||
|
for _, v := range dst {
|
||||||
|
es = append(es, ToElement(v.(mo.Reference), l.Prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
return es, nil
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 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 list
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ToParts(p string) []string {
|
||||||
|
p = path.Clean(p)
|
||||||
|
if p == "/" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) > 0 {
|
||||||
|
// Prefix ./ if relative
|
||||||
|
if p[0] != '/' && p[0] != '.' {
|
||||||
|
p = "./" + p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ps := strings.Split(p, "/")
|
||||||
|
if ps[0] == "" {
|
||||||
|
// Start at root
|
||||||
|
ps = ps[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return ps
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthorizationManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuthorizationManager(c *vim25.Client) *AuthorizationManager {
|
||||||
|
m := AuthorizationManager{
|
||||||
|
Common: NewCommon(c, *c.ServiceContent.AuthorizationManager),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthorizationRoleList []types.AuthorizationRole
|
||||||
|
|
||||||
|
func (l AuthorizationRoleList) ById(id int32) *types.AuthorizationRole {
|
||||||
|
for _, role := range l {
|
||||||
|
if role.RoleId == id {
|
||||||
|
return &role
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l AuthorizationRoleList) ByName(name string) *types.AuthorizationRole {
|
||||||
|
for _, role := range l {
|
||||||
|
if role.Name == name {
|
||||||
|
return &role
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AuthorizationManager) RoleList(ctx context.Context) (AuthorizationRoleList, error) {
|
||||||
|
var am mo.AuthorizationManager
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"roleList"}, &am)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return AuthorizationRoleList(am.RoleList), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AuthorizationManager) RetrieveEntityPermissions(ctx context.Context, entity types.ManagedObjectReference, inherited bool) ([]types.Permission, error) {
|
||||||
|
req := types.RetrieveEntityPermissions{
|
||||||
|
This: m.Reference(),
|
||||||
|
Entity: entity,
|
||||||
|
Inherited: inherited,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.RetrieveEntityPermissions(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AuthorizationManager) RemoveEntityPermission(ctx context.Context, entity types.ManagedObjectReference, user string, isGroup bool) error {
|
||||||
|
req := types.RemoveEntityPermission{
|
||||||
|
This: m.Reference(),
|
||||||
|
Entity: entity,
|
||||||
|
User: user,
|
||||||
|
IsGroup: isGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RemoveEntityPermission(ctx, m.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AuthorizationManager) SetEntityPermissions(ctx context.Context, entity types.ManagedObjectReference, permission []types.Permission) error {
|
||||||
|
req := types.SetEntityPermissions{
|
||||||
|
This: m.Reference(),
|
||||||
|
Entity: entity,
|
||||||
|
Permission: permission,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.SetEntityPermissions(ctx, m.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AuthorizationManager) RetrieveRolePermissions(ctx context.Context, id int32) ([]types.Permission, error) {
|
||||||
|
req := types.RetrieveRolePermissions{
|
||||||
|
This: m.Reference(),
|
||||||
|
RoleId: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.RetrieveRolePermissions(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AuthorizationManager) RetrieveAllPermissions(ctx context.Context) ([]types.Permission, error) {
|
||||||
|
req := types.RetrieveAllPermissions{
|
||||||
|
This: m.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.RetrieveAllPermissions(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AuthorizationManager) AddRole(ctx context.Context, name string, ids []string) (int32, error) {
|
||||||
|
req := types.AddAuthorizationRole{
|
||||||
|
This: m.Reference(),
|
||||||
|
Name: name,
|
||||||
|
PrivIds: ids,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.AddAuthorizationRole(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AuthorizationManager) RemoveRole(ctx context.Context, id int32, failIfUsed bool) error {
|
||||||
|
req := types.RemoveAuthorizationRole{
|
||||||
|
This: m.Reference(),
|
||||||
|
RoleId: id,
|
||||||
|
FailIfUsed: failIfUsed,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RemoveAuthorizationRole(ctx, m.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AuthorizationManager) UpdateRole(ctx context.Context, id int32, name string, ids []string) error {
|
||||||
|
req := types.UpdateAuthorizationRole{
|
||||||
|
This: m.Reference(),
|
||||||
|
RoleId: id,
|
||||||
|
NewName: name,
|
||||||
|
PrivIds: ids,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateAuthorizationRole(ctx, m.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
86
vendor/github.com/vmware/govmomi/object/authorization_manager_internal.go
generated
vendored
Normal file
86
vendor/github.com/vmware/govmomi/object/authorization_manager_internal.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 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/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DisabledMethodRequest struct {
|
||||||
|
Method string `xml:"method"`
|
||||||
|
Reason string `xml:"reasonId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type disableMethodsRequest struct {
|
||||||
|
This types.ManagedObjectReference `xml:"_this"`
|
||||||
|
Entity []types.ManagedObjectReference `xml:"entity"`
|
||||||
|
Method []DisabledMethodRequest `xml:"method"`
|
||||||
|
Source string `xml:"sourceId"`
|
||||||
|
Scope bool `xml:"sessionScope,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type disableMethodsBody struct {
|
||||||
|
Req *disableMethodsRequest `xml:"urn:internalvim25 DisableMethods,omitempty"`
|
||||||
|
Res interface{} `xml:"urn:vim25 DisableMethodsResponse,omitempty"`
|
||||||
|
Err *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *disableMethodsBody) Fault() *soap.Fault { return b.Err }
|
||||||
|
|
||||||
|
func (m AuthorizationManager) DisableMethods(ctx context.Context, entity []types.ManagedObjectReference, method []DisabledMethodRequest, source string) error {
|
||||||
|
var reqBody, resBody disableMethodsBody
|
||||||
|
|
||||||
|
reqBody.Req = &disableMethodsRequest{
|
||||||
|
This: m.Reference(),
|
||||||
|
Entity: entity,
|
||||||
|
Method: method,
|
||||||
|
Source: source,
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.Client().RoundTrip(ctx, &reqBody, &resBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
type enableMethodsRequest struct {
|
||||||
|
This types.ManagedObjectReference `xml:"_this"`
|
||||||
|
Entity []types.ManagedObjectReference `xml:"entity"`
|
||||||
|
Method []string `xml:"method"`
|
||||||
|
Source string `xml:"sourceId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type enableMethodsBody struct {
|
||||||
|
Req *enableMethodsRequest `xml:"urn:internalvim25 EnableMethods,omitempty"`
|
||||||
|
Res interface{} `xml:"urn:vim25 EnableMethodsResponse,omitempty"`
|
||||||
|
Err *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *enableMethodsBody) Fault() *soap.Fault { return b.Err }
|
||||||
|
|
||||||
|
func (m AuthorizationManager) EnableMethods(ctx context.Context, entity []types.ManagedObjectReference, method []string, source string) error {
|
||||||
|
var reqBody, resBody enableMethodsBody
|
||||||
|
|
||||||
|
reqBody.Req = &enableMethodsRequest{
|
||||||
|
This: m.Reference(),
|
||||||
|
Entity: entity,
|
||||||
|
Method: method,
|
||||||
|
Source: source,
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.Client().RoundTrip(ctx, &reqBody, &resBody)
|
||||||
|
}
|
86
vendor/github.com/vmware/govmomi/object/cluster_compute_resource.go
generated
vendored
Normal file
86
vendor/github.com/vmware/govmomi/object/cluster_compute_resource.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClusterComputeResource struct {
|
||||||
|
ComputeResource
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClusterComputeResource(c *vim25.Client, ref types.ManagedObjectReference) *ClusterComputeResource {
|
||||||
|
return &ClusterComputeResource{
|
||||||
|
ComputeResource: *NewComputeResource(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ClusterComputeResource) ReconfigureCluster(ctx context.Context, spec types.ClusterConfigSpec) (*Task, error) {
|
||||||
|
req := types.ReconfigureCluster_Task{
|
||||||
|
This: c.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
Modify: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ReconfigureCluster_Task(ctx, c.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(c.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ClusterComputeResource) AddHost(ctx context.Context, spec types.HostConnectSpec, asConnected bool, license *string, resourcePool *types.ManagedObjectReference) (*Task, error) {
|
||||||
|
req := types.AddHost_Task{
|
||||||
|
This: c.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
AsConnected: asConnected,
|
||||||
|
}
|
||||||
|
|
||||||
|
if license != nil {
|
||||||
|
req.License = *license
|
||||||
|
}
|
||||||
|
|
||||||
|
if resourcePool != nil {
|
||||||
|
req.ResourcePool = resourcePool
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.AddHost_Task(ctx, c.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(c.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ClusterComputeResource) Destroy(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.Destroy_Task{
|
||||||
|
This: c.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.Destroy_Task(ctx, c.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(c.c, res.Returnval), nil
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/property"
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotSupported = errors.New("product/version specific feature not supported by target")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Common contains the fields and functions common to all objects.
|
||||||
|
type Common struct {
|
||||||
|
InventoryPath string
|
||||||
|
|
||||||
|
c *vim25.Client
|
||||||
|
r types.ManagedObjectReference
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Common) String() string {
|
||||||
|
ref := fmt.Sprintf("%v", c.Reference())
|
||||||
|
|
||||||
|
if c.InventoryPath == "" {
|
||||||
|
return ref
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s @ %s", ref, c.InventoryPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommon(c *vim25.Client, r types.ManagedObjectReference) Common {
|
||||||
|
return Common{c: c, r: r}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Common) Reference() types.ManagedObjectReference {
|
||||||
|
return c.r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Common) Client() *vim25.Client {
|
||||||
|
return c.c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the base name of the InventoryPath field
|
||||||
|
func (c Common) Name() string {
|
||||||
|
if c.InventoryPath == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return path.Base(c.InventoryPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Common) SetInventoryPath(p string) {
|
||||||
|
c.InventoryPath = p
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObjectName returns the base name of the InventoryPath field if set,
|
||||||
|
// otherwise fetches the mo.ManagedEntity.Name field via the property collector.
|
||||||
|
func (c Common) ObjectName(ctx context.Context) (string, error) {
|
||||||
|
var o mo.ManagedEntity
|
||||||
|
|
||||||
|
err := c.Properties(ctx, c.Reference(), []string{"name"}, &o)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return o.Name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Common) Properties(ctx context.Context, r types.ManagedObjectReference, ps []string, dst interface{}) error {
|
||||||
|
return property.DefaultCollector(c.c).RetrieveOne(ctx, r, ps, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Common) Destroy(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.Destroy_Task{
|
||||||
|
This: c.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.Destroy_Task(ctx, c.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(c.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Common) Rename(ctx context.Context, name string) (*Task, error) {
|
||||||
|
req := types.Rename_Task{
|
||||||
|
This: c.Reference(),
|
||||||
|
NewName: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.Rename_Task(ctx, c.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(c.c, res.Returnval), nil
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/property"
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ComputeResource struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewComputeResource(c *vim25.Client, ref types.ManagedObjectReference) *ComputeResource {
|
||||||
|
return &ComputeResource{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ComputeResource) Hosts(ctx context.Context) ([]*HostSystem, error) {
|
||||||
|
var cr mo.ComputeResource
|
||||||
|
|
||||||
|
err := c.Properties(ctx, c.Reference(), []string{"host"}, &cr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cr.Host) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var hs []mo.HostSystem
|
||||||
|
pc := property.DefaultCollector(c.Client())
|
||||||
|
err = pc.Retrieve(ctx, cr.Host, []string{"name"}, &hs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var hosts []*HostSystem
|
||||||
|
|
||||||
|
for _, h := range hs {
|
||||||
|
host := NewHostSystem(c.Client(), h.Reference())
|
||||||
|
host.InventoryPath = path.Join(c.InventoryPath, h.Name)
|
||||||
|
hosts = append(hosts, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hosts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ComputeResource) Datastores(ctx context.Context) ([]*Datastore, error) {
|
||||||
|
var cr mo.ComputeResource
|
||||||
|
|
||||||
|
err := c.Properties(ctx, c.Reference(), []string{"datastore"}, &cr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var dss []*Datastore
|
||||||
|
for _, ref := range cr.Datastore {
|
||||||
|
ds := NewDatastore(c.c, ref)
|
||||||
|
dss = append(dss, ds)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dss, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ComputeResource) ResourcePool(ctx context.Context) (*ResourcePool, error) {
|
||||||
|
var cr mo.ComputeResource
|
||||||
|
|
||||||
|
err := c.Properties(ctx, c.Reference(), []string{"resourcePool"}, &cr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewResourcePool(c.c, *cr.ResourcePool), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ComputeResource) Reconfigure(ctx context.Context, spec types.BaseComputeResourceConfigSpec, modify bool) (*Task, error) {
|
||||||
|
req := types.ReconfigureComputeResource_Task{
|
||||||
|
This: c.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
Modify: modify,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ReconfigureComputeResource_Task(ctx, c.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(c.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ComputeResource) Destroy(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.Destroy_Task{
|
||||||
|
This: c.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.Destroy_Task(ctx, c.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(c.c, res.Returnval), nil
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrKeyNameNotFound = errors.New("key name not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomFieldsManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCustomFieldsManager wraps NewCustomFieldsManager, returning ErrNotSupported
|
||||||
|
// when the client is not connected to a vCenter instance.
|
||||||
|
func GetCustomFieldsManager(c *vim25.Client) (*CustomFieldsManager, error) {
|
||||||
|
if c.ServiceContent.CustomFieldsManager == nil {
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
return NewCustomFieldsManager(c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCustomFieldsManager(c *vim25.Client) *CustomFieldsManager {
|
||||||
|
m := CustomFieldsManager{
|
||||||
|
Common: NewCommon(c, *c.ServiceContent.CustomFieldsManager),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m CustomFieldsManager) Add(ctx context.Context, name string, moType string, fieldDefPolicy *types.PrivilegePolicyDef, fieldPolicy *types.PrivilegePolicyDef) (*types.CustomFieldDef, error) {
|
||||||
|
req := types.AddCustomFieldDef{
|
||||||
|
This: m.Reference(),
|
||||||
|
Name: name,
|
||||||
|
MoType: moType,
|
||||||
|
FieldDefPolicy: fieldDefPolicy,
|
||||||
|
FieldPolicy: fieldPolicy,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.AddCustomFieldDef(ctx, m.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m CustomFieldsManager) Remove(ctx context.Context, key int32) error {
|
||||||
|
req := types.RemoveCustomFieldDef{
|
||||||
|
This: m.Reference(),
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RemoveCustomFieldDef(ctx, m.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m CustomFieldsManager) Rename(ctx context.Context, key int32, name string) error {
|
||||||
|
req := types.RenameCustomFieldDef{
|
||||||
|
This: m.Reference(),
|
||||||
|
Key: key,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RenameCustomFieldDef(ctx, m.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m CustomFieldsManager) Set(ctx context.Context, entity types.ManagedObjectReference, key int32, value string) error {
|
||||||
|
req := types.SetField{
|
||||||
|
This: m.Reference(),
|
||||||
|
Entity: entity,
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.SetField(ctx, m.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m CustomFieldsManager) Field(ctx context.Context) ([]types.CustomFieldDef, error) {
|
||||||
|
var fm mo.CustomFieldsManager
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"field"}, &fm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fm.Field, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m CustomFieldsManager) FindKey(ctx context.Context, key string) (int32, error) {
|
||||||
|
field, err := m.Field(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, def := range field {
|
||||||
|
if def.Name == key {
|
||||||
|
return def.Key, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
k, err := strconv.Atoi(key)
|
||||||
|
if err == nil {
|
||||||
|
// assume literal int key
|
||||||
|
return int32(k), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1, ErrKeyNameNotFound
|
||||||
|
}
|
166
vendor/github.com/vmware/govmomi/object/customization_spec_manager.go
generated
vendored
Normal file
166
vendor/github.com/vmware/govmomi/object/customization_spec_manager.go
generated
vendored
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomizationSpecManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCustomizationSpecManager(c *vim25.Client) *CustomizationSpecManager {
|
||||||
|
cs := CustomizationSpecManager{
|
||||||
|
Common: NewCommon(c, *c.ServiceContent.CustomizationSpecManager),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs CustomizationSpecManager) DoesCustomizationSpecExist(ctx context.Context, name string) (bool, error) {
|
||||||
|
req := types.DoesCustomizationSpecExist{
|
||||||
|
This: cs.Reference(),
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.DoesCustomizationSpecExist(ctx, cs.c, &req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs CustomizationSpecManager) GetCustomizationSpec(ctx context.Context, name string) (*types.CustomizationSpecItem, error) {
|
||||||
|
req := types.GetCustomizationSpec{
|
||||||
|
This: cs.Reference(),
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.GetCustomizationSpec(ctx, cs.c, &req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs CustomizationSpecManager) CreateCustomizationSpec(ctx context.Context, item types.CustomizationSpecItem) error {
|
||||||
|
req := types.CreateCustomizationSpec{
|
||||||
|
This: cs.Reference(),
|
||||||
|
Item: item,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.CreateCustomizationSpec(ctx, cs.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs CustomizationSpecManager) OverwriteCustomizationSpec(ctx context.Context, item types.CustomizationSpecItem) error {
|
||||||
|
req := types.OverwriteCustomizationSpec{
|
||||||
|
This: cs.Reference(),
|
||||||
|
Item: item,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.OverwriteCustomizationSpec(ctx, cs.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs CustomizationSpecManager) DeleteCustomizationSpec(ctx context.Context, name string) error {
|
||||||
|
req := types.DeleteCustomizationSpec{
|
||||||
|
This: cs.Reference(),
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.DeleteCustomizationSpec(ctx, cs.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs CustomizationSpecManager) DuplicateCustomizationSpec(ctx context.Context, name string, newName string) error {
|
||||||
|
req := types.DuplicateCustomizationSpec{
|
||||||
|
This: cs.Reference(),
|
||||||
|
Name: name,
|
||||||
|
NewName: newName,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.DuplicateCustomizationSpec(ctx, cs.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs CustomizationSpecManager) RenameCustomizationSpec(ctx context.Context, name string, newName string) error {
|
||||||
|
req := types.RenameCustomizationSpec{
|
||||||
|
This: cs.Reference(),
|
||||||
|
Name: name,
|
||||||
|
NewName: newName,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RenameCustomizationSpec(ctx, cs.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs CustomizationSpecManager) CustomizationSpecItemToXml(ctx context.Context, item types.CustomizationSpecItem) (string, error) {
|
||||||
|
req := types.CustomizationSpecItemToXml{
|
||||||
|
This: cs.Reference(),
|
||||||
|
Item: item,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CustomizationSpecItemToXml(ctx, cs.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs CustomizationSpecManager) XmlToCustomizationSpecItem(ctx context.Context, xml string) (*types.CustomizationSpecItem, error) {
|
||||||
|
req := types.XmlToCustomizationSpecItem{
|
||||||
|
This: cs.Reference(),
|
||||||
|
SpecItemXml: xml,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.XmlToCustomizationSpecItem(ctx, cs.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DatacenterFolders struct {
|
||||||
|
VmFolder *Folder
|
||||||
|
HostFolder *Folder
|
||||||
|
DatastoreFolder *Folder
|
||||||
|
NetworkFolder *Folder
|
||||||
|
}
|
||||||
|
|
||||||
|
type Datacenter struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDatacenter(c *vim25.Client, ref types.ManagedObjectReference) *Datacenter {
|
||||||
|
return &Datacenter{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Datacenter) Folders(ctx context.Context) (*DatacenterFolders, error) {
|
||||||
|
var md mo.Datacenter
|
||||||
|
|
||||||
|
ps := []string{"name", "vmFolder", "hostFolder", "datastoreFolder", "networkFolder"}
|
||||||
|
err := d.Properties(ctx, d.Reference(), ps, &md)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
df := &DatacenterFolders{
|
||||||
|
VmFolder: NewFolder(d.c, md.VmFolder),
|
||||||
|
HostFolder: NewFolder(d.c, md.HostFolder),
|
||||||
|
DatastoreFolder: NewFolder(d.c, md.DatastoreFolder),
|
||||||
|
NetworkFolder: NewFolder(d.c, md.NetworkFolder),
|
||||||
|
}
|
||||||
|
|
||||||
|
paths := []struct {
|
||||||
|
name string
|
||||||
|
path *string
|
||||||
|
}{
|
||||||
|
{"vm", &df.VmFolder.InventoryPath},
|
||||||
|
{"host", &df.HostFolder.InventoryPath},
|
||||||
|
{"datastore", &df.DatastoreFolder.InventoryPath},
|
||||||
|
{"network", &df.NetworkFolder.InventoryPath},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range paths {
|
||||||
|
*p.path = fmt.Sprintf("/%s/%s", md.Name, p.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return df, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Datacenter) Destroy(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.Destroy_Task{
|
||||||
|
This: d.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.Destroy_Task(ctx, d.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(d.c, res.Returnval), nil
|
||||||
|
}
|
|
@ -0,0 +1,438 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015-2016 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 (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/property"
|
||||||
|
"github.com/vmware/govmomi/session"
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DatastoreNoSuchDirectoryError is returned when a directory could not be found.
|
||||||
|
type DatastoreNoSuchDirectoryError struct {
|
||||||
|
verb string
|
||||||
|
subject string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DatastoreNoSuchDirectoryError) Error() string {
|
||||||
|
return fmt.Sprintf("cannot %s '%s': No such directory", e.verb, e.subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatastoreNoSuchFileError is returned when a file could not be found.
|
||||||
|
type DatastoreNoSuchFileError struct {
|
||||||
|
verb string
|
||||||
|
subject string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DatastoreNoSuchFileError) Error() string {
|
||||||
|
return fmt.Sprintf("cannot %s '%s': No such file", e.verb, e.subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Datastore struct {
|
||||||
|
Common
|
||||||
|
|
||||||
|
DatacenterPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDatastore(c *vim25.Client, ref types.ManagedObjectReference) *Datastore {
|
||||||
|
return &Datastore{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Datastore) Path(path string) string {
|
||||||
|
return (&DatastorePath{
|
||||||
|
Datastore: d.Name(),
|
||||||
|
Path: path,
|
||||||
|
}).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewURL constructs a url.URL with the given file path for datastore access over HTTP.
|
||||||
|
func (d Datastore) NewURL(path string) *url.URL {
|
||||||
|
u := d.c.URL()
|
||||||
|
|
||||||
|
return &url.URL{
|
||||||
|
Scheme: u.Scheme,
|
||||||
|
Host: u.Host,
|
||||||
|
Path: fmt.Sprintf("/folder/%s", path),
|
||||||
|
RawQuery: url.Values{
|
||||||
|
"dcPath": []string{d.DatacenterPath},
|
||||||
|
"dsName": []string{d.Name()},
|
||||||
|
}.Encode(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL is deprecated, use NewURL instead.
|
||||||
|
func (d Datastore) URL(ctx context.Context, dc *Datacenter, path string) (*url.URL, error) {
|
||||||
|
return d.NewURL(path), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Datastore) Browser(ctx context.Context) (*HostDatastoreBrowser, error) {
|
||||||
|
var do mo.Datastore
|
||||||
|
|
||||||
|
err := d.Properties(ctx, d.Reference(), []string{"browser"}, &do)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostDatastoreBrowser(d.c, do.Browser), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Datastore) useServiceTicket() bool {
|
||||||
|
// If connected to workstation, service ticketing not supported
|
||||||
|
// If connected to ESX, service ticketing not needed
|
||||||
|
if !d.c.IsVC() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
key := "GOVMOMI_USE_SERVICE_TICKET"
|
||||||
|
|
||||||
|
val := d.c.URL().Query().Get(key)
|
||||||
|
if val == "" {
|
||||||
|
val = os.Getenv(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val == "1" || val == "true" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Datastore) useServiceTicketHostName(name string) bool {
|
||||||
|
// No need if talking directly to ESX.
|
||||||
|
if !d.c.IsVC() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If version happens to be < 5.1
|
||||||
|
if name == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the HostSystem is using DHCP on a network without dynamic DNS,
|
||||||
|
// HostSystem.Config.Network.DnsConfig.HostName is set to "localhost" by default.
|
||||||
|
// This resolves to "localhost.localdomain" by default via /etc/hosts on ESX.
|
||||||
|
// In that case, we will stick with the HostSystem.Name which is the IP address that
|
||||||
|
// was used to connect the host to VC.
|
||||||
|
if name == "localhost.localdomain" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Still possible to have HostName that don't resolve via DNS,
|
||||||
|
// so we default to false.
|
||||||
|
key := "GOVMOMI_USE_SERVICE_TICKET_HOSTNAME"
|
||||||
|
|
||||||
|
val := d.c.URL().Query().Get(key)
|
||||||
|
if val == "" {
|
||||||
|
val = os.Getenv(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val == "1" || val == "true" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type datastoreServiceTicketHostKey struct{}
|
||||||
|
|
||||||
|
// HostContext returns a Context where the given host will be used for datastore HTTP access
|
||||||
|
// via the ServiceTicket method.
|
||||||
|
func (d Datastore) HostContext(ctx context.Context, host *HostSystem) context.Context {
|
||||||
|
return context.WithValue(ctx, datastoreServiceTicketHostKey{}, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceTicket obtains a ticket via AcquireGenericServiceTicket and returns it an http.Cookie with the url.URL
|
||||||
|
// that can be used along with the ticket cookie to access the given path. An host is chosen at random unless the
|
||||||
|
// the given Context was created with a specific host via the HostContext method.
|
||||||
|
func (d Datastore) ServiceTicket(ctx context.Context, path string, method string) (*url.URL, *http.Cookie, error) {
|
||||||
|
u := d.NewURL(path)
|
||||||
|
|
||||||
|
host, ok := ctx.Value(datastoreServiceTicketHostKey{}).(*HostSystem)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
if !d.useServiceTicket() {
|
||||||
|
return u, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts, err := d.AttachedHosts(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hosts) == 0 {
|
||||||
|
// Fallback to letting vCenter choose a host
|
||||||
|
return u, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick a random attached host
|
||||||
|
host = hosts[rand.Intn(len(hosts))]
|
||||||
|
}
|
||||||
|
|
||||||
|
ips, err := host.ManagementIPs(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ips) > 0 {
|
||||||
|
// prefer a ManagementIP
|
||||||
|
u.Host = ips[0].String()
|
||||||
|
} else {
|
||||||
|
// fallback to inventory name
|
||||||
|
u.Host, err = host.ObjectName(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VC datacenter path will not be valid against ESX
|
||||||
|
q := u.Query()
|
||||||
|
delete(q, "dcPath")
|
||||||
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
spec := types.SessionManagerHttpServiceRequestSpec{
|
||||||
|
Url: u.String(),
|
||||||
|
// See SessionManagerHttpServiceRequestSpecMethod enum
|
||||||
|
Method: fmt.Sprintf("http%s%s", method[0:1], strings.ToLower(method[1:])),
|
||||||
|
}
|
||||||
|
|
||||||
|
sm := session.NewManager(d.Client())
|
||||||
|
|
||||||
|
ticket, err := sm.AcquireGenericServiceTicket(ctx, &spec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cookie := &http.Cookie{
|
||||||
|
Name: "vmware_cgi_ticket",
|
||||||
|
Value: ticket.Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.useServiceTicketHostName(ticket.HostName) {
|
||||||
|
u.Host = ticket.HostName
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Client().SetThumbprint(u.Host, ticket.SslThumbprint)
|
||||||
|
|
||||||
|
return u, cookie, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Datastore) uploadTicket(ctx context.Context, path string, param *soap.Upload) (*url.URL, *soap.Upload, error) {
|
||||||
|
p := soap.DefaultUpload
|
||||||
|
if param != nil {
|
||||||
|
p = *param // copy
|
||||||
|
}
|
||||||
|
|
||||||
|
u, ticket, err := d.ServiceTicket(ctx, path, p.Method)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Ticket = ticket
|
||||||
|
|
||||||
|
return u, &p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Datastore) downloadTicket(ctx context.Context, path string, param *soap.Download) (*url.URL, *soap.Download, error) {
|
||||||
|
p := soap.DefaultDownload
|
||||||
|
if param != nil {
|
||||||
|
p = *param // copy
|
||||||
|
}
|
||||||
|
|
||||||
|
u, ticket, err := d.ServiceTicket(ctx, path, p.Method)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Ticket = ticket
|
||||||
|
|
||||||
|
return u, &p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload via soap.Upload with an http service ticket
|
||||||
|
func (d Datastore) Upload(ctx context.Context, f io.Reader, path string, param *soap.Upload) error {
|
||||||
|
u, p, err := d.uploadTicket(ctx, path, param)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.Client().Upload(f, u, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadFile via soap.Upload with an http service ticket
|
||||||
|
func (d Datastore) UploadFile(ctx context.Context, file string, path string, param *soap.Upload) error {
|
||||||
|
u, p, err := d.uploadTicket(ctx, path, param)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.Client().UploadFile(file, u, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download via soap.Download with an http service ticket
|
||||||
|
func (d Datastore) Download(ctx context.Context, path string, param *soap.Download) (io.ReadCloser, int64, error) {
|
||||||
|
u, p, err := d.downloadTicket(ctx, path, param)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
return d.Client().Download(u, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadFile via soap.Download with an http service ticket
|
||||||
|
func (d Datastore) DownloadFile(ctx context.Context, path string, file string, param *soap.Download) error {
|
||||||
|
u, p, err := d.downloadTicket(ctx, path, param)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.Client().DownloadFile(file, u, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttachedHosts returns hosts that have this Datastore attached, accessible and writable.
|
||||||
|
func (d Datastore) AttachedHosts(ctx context.Context) ([]*HostSystem, error) {
|
||||||
|
var ds mo.Datastore
|
||||||
|
var hosts []*HostSystem
|
||||||
|
|
||||||
|
pc := property.DefaultCollector(d.Client())
|
||||||
|
err := pc.RetrieveOne(ctx, d.Reference(), []string{"host"}, &ds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mounts := make(map[types.ManagedObjectReference]types.DatastoreHostMount)
|
||||||
|
var refs []types.ManagedObjectReference
|
||||||
|
for _, host := range ds.Host {
|
||||||
|
refs = append(refs, host.Key)
|
||||||
|
mounts[host.Key] = host
|
||||||
|
}
|
||||||
|
|
||||||
|
var hs []mo.HostSystem
|
||||||
|
err = pc.Retrieve(ctx, refs, []string{"runtime.connectionState", "runtime.powerState"}, &hs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range hs {
|
||||||
|
if host.Runtime.ConnectionState == types.HostSystemConnectionStateConnected &&
|
||||||
|
host.Runtime.PowerState == types.HostSystemPowerStatePoweredOn {
|
||||||
|
|
||||||
|
mount := mounts[host.Reference()]
|
||||||
|
info := mount.MountInfo
|
||||||
|
|
||||||
|
if *info.Mounted && *info.Accessible && info.AccessMode == string(types.HostMountModeReadWrite) {
|
||||||
|
hosts = append(hosts, NewHostSystem(d.Client(), mount.Key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hosts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttachedClusterHosts returns hosts that have this Datastore attached, accessible and writable and are members of the given cluster.
|
||||||
|
func (d Datastore) AttachedClusterHosts(ctx context.Context, cluster *ComputeResource) ([]*HostSystem, error) {
|
||||||
|
var hosts []*HostSystem
|
||||||
|
|
||||||
|
clusterHosts, err := cluster.Hosts(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
attachedHosts, err := d.AttachedHosts(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
refs := make(map[types.ManagedObjectReference]bool)
|
||||||
|
for _, host := range attachedHosts {
|
||||||
|
refs[host.Reference()] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range clusterHosts {
|
||||||
|
if refs[host.Reference()] {
|
||||||
|
hosts = append(hosts, host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hosts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Datastore) Stat(ctx context.Context, file string) (types.BaseFileInfo, error) {
|
||||||
|
b, err := d.Browser(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
spec := types.HostDatastoreBrowserSearchSpec{
|
||||||
|
Details: &types.FileQueryFlags{
|
||||||
|
FileType: true,
|
||||||
|
FileSize: true,
|
||||||
|
Modification: true,
|
||||||
|
FileOwner: types.NewBool(true),
|
||||||
|
},
|
||||||
|
MatchPattern: []string{path.Base(file)},
|
||||||
|
}
|
||||||
|
|
||||||
|
dsPath := d.Path(path.Dir(file))
|
||||||
|
task, err := b.SearchDatastore(ctx, dsPath, &spec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := task.WaitForResult(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
if info == nil || info.Error != nil {
|
||||||
|
_, ok := info.Error.Fault.(*types.FileNotFound)
|
||||||
|
if ok {
|
||||||
|
// FileNotFound means the base path doesn't exist.
|
||||||
|
return nil, DatastoreNoSuchDirectoryError{"stat", dsPath}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := info.Result.(types.HostDatastoreBrowserSearchResults)
|
||||||
|
if len(res.File) == 0 {
|
||||||
|
// File doesn't exist
|
||||||
|
return nil, DatastoreNoSuchFileError{"stat", d.Path(file)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.File[0], nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the type of file system volume.
|
||||||
|
func (d Datastore) Type(ctx context.Context) (types.HostFileSystemVolumeFileSystemType, error) {
|
||||||
|
var mds mo.Datastore
|
||||||
|
|
||||||
|
if err := d.Properties(ctx, d.Reference(), []string{"summary.type"}, &mds); err != nil {
|
||||||
|
return types.HostFileSystemVolumeFileSystemType(""), err
|
||||||
|
}
|
||||||
|
return types.HostFileSystemVolumeFileSystemType(mds.Summary.Type), nil
|
||||||
|
}
|
|
@ -0,0 +1,408 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016-2017 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 (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DatastoreFile implements io.Reader, io.Seeker and io.Closer interfaces for datastore file access.
|
||||||
|
type DatastoreFile struct {
|
||||||
|
d Datastore
|
||||||
|
ctx context.Context
|
||||||
|
name string
|
||||||
|
|
||||||
|
buf io.Reader
|
||||||
|
body io.ReadCloser
|
||||||
|
length int64
|
||||||
|
offset struct {
|
||||||
|
read, seek int64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open opens the named file relative to the Datastore.
|
||||||
|
func (d Datastore) Open(ctx context.Context, name string) (*DatastoreFile, error) {
|
||||||
|
return &DatastoreFile{
|
||||||
|
d: d,
|
||||||
|
name: name,
|
||||||
|
length: -1,
|
||||||
|
ctx: ctx,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads up to len(b) bytes from the DatastoreFile.
|
||||||
|
func (f *DatastoreFile) Read(b []byte) (int, error) {
|
||||||
|
if f.offset.read != f.offset.seek {
|
||||||
|
// A Seek() call changed the offset, we need to issue a new GET
|
||||||
|
_ = f.Close()
|
||||||
|
|
||||||
|
f.offset.read = f.offset.seek
|
||||||
|
} else if f.buf != nil {
|
||||||
|
// f.buf + f behaves like an io.MultiReader
|
||||||
|
n, err := f.buf.Read(b)
|
||||||
|
if err == io.EOF {
|
||||||
|
f.buf = nil // buffer has been drained
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := f.get()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := body.Read(b)
|
||||||
|
|
||||||
|
f.offset.read += int64(n)
|
||||||
|
f.offset.seek += int64(n)
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the DatastoreFile.
|
||||||
|
func (f *DatastoreFile) Close() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if f.body != nil {
|
||||||
|
err = f.body.Close()
|
||||||
|
f.body = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
f.buf = nil
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek sets the offset for the next Read on the DatastoreFile.
|
||||||
|
func (f *DatastoreFile) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
switch whence {
|
||||||
|
case io.SeekStart:
|
||||||
|
case io.SeekCurrent:
|
||||||
|
offset += f.offset.seek
|
||||||
|
case io.SeekEnd:
|
||||||
|
if f.length < 0 {
|
||||||
|
_, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset += f.length
|
||||||
|
default:
|
||||||
|
return 0, errors.New("Seek: invalid whence")
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow negative SeekStart for initial Range request
|
||||||
|
if offset < 0 {
|
||||||
|
return 0, errors.New("Seek: invalid offset")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.offset.seek = offset
|
||||||
|
|
||||||
|
return offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileStat struct {
|
||||||
|
file *DatastoreFile
|
||||||
|
header http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fileStat) Name() string {
|
||||||
|
return path.Base(s.file.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fileStat) Size() int64 {
|
||||||
|
return s.file.length
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fileStat) Mode() os.FileMode {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fileStat) ModTime() time.Time {
|
||||||
|
return time.Now() // no Last-Modified
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fileStat) IsDir() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fileStat) Sys() interface{} {
|
||||||
|
return s.header
|
||||||
|
}
|
||||||
|
|
||||||
|
func statusError(res *http.Response) error {
|
||||||
|
if res.StatusCode == http.StatusNotFound {
|
||||||
|
return os.ErrNotExist
|
||||||
|
}
|
||||||
|
return errors.New(res.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat returns the os.FileInfo interface describing file.
|
||||||
|
func (f *DatastoreFile) Stat() (os.FileInfo, error) {
|
||||||
|
// TODO: consider using Datastore.Stat() instead
|
||||||
|
u, p, err := f.d.downloadTicket(f.ctx, f.name, &soap.Download{Method: "HEAD"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := f.d.Client().DownloadRequest(u, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
return nil, statusError(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.length = res.ContentLength
|
||||||
|
|
||||||
|
return &fileStat{f, res.Header}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *DatastoreFile) get() (io.Reader, error) {
|
||||||
|
if f.body != nil {
|
||||||
|
return f.body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
u, p, err := f.d.downloadTicket(f.ctx, f.name, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.offset.read != 0 {
|
||||||
|
p.Headers = map[string]string{
|
||||||
|
"Range": fmt.Sprintf("bytes=%d-", f.offset.read),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := f.d.Client().DownloadRequest(u, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch res.StatusCode {
|
||||||
|
case http.StatusOK:
|
||||||
|
f.length = res.ContentLength
|
||||||
|
case http.StatusPartialContent:
|
||||||
|
var start, end int
|
||||||
|
cr := res.Header.Get("Content-Range")
|
||||||
|
_, err = fmt.Sscanf(cr, "bytes %d-%d/%d", &start, &end, &f.length)
|
||||||
|
if err != nil {
|
||||||
|
f.length = -1
|
||||||
|
}
|
||||||
|
case http.StatusRequestedRangeNotSatisfiable:
|
||||||
|
// ok: Read() will return io.EOF
|
||||||
|
default:
|
||||||
|
return nil, statusError(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.length < 0 {
|
||||||
|
_ = res.Body.Close()
|
||||||
|
return nil, errors.New("unable to determine file size")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.body = res.Body
|
||||||
|
|
||||||
|
return f.body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lastIndexLines(s []byte, line *int, include func(l int, m string) bool) (int64, bool) {
|
||||||
|
i := len(s) - 1
|
||||||
|
done := false
|
||||||
|
|
||||||
|
for i > 0 {
|
||||||
|
o := bytes.LastIndexByte(s[:i], '\n')
|
||||||
|
if o < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := string(s[o+1 : i+1])
|
||||||
|
if !include(*line, msg) {
|
||||||
|
done = true
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
i = o
|
||||||
|
*line++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return int64(i), done
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tail seeks to the position of the last N lines of the file.
|
||||||
|
func (f *DatastoreFile) Tail(n int) error {
|
||||||
|
return f.TailFunc(n, func(line int, _ string) bool { return n > line })
|
||||||
|
}
|
||||||
|
|
||||||
|
// TailFunc will seek backwards in the datastore file until it hits a line that does
|
||||||
|
// not satisfy the supplied `include` function.
|
||||||
|
func (f *DatastoreFile) TailFunc(lines int, include func(line int, message string) bool) error {
|
||||||
|
// Read the file in reverse using bsize chunks
|
||||||
|
const bsize = int64(1024 * 16)
|
||||||
|
|
||||||
|
fsize, err := f.Seek(0, io.SeekEnd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if lines == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk := int64(-1)
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, bsize))
|
||||||
|
line := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
var eof bool
|
||||||
|
var pos int64
|
||||||
|
|
||||||
|
nread := bsize
|
||||||
|
|
||||||
|
offset := chunk * bsize
|
||||||
|
remain := fsize + offset
|
||||||
|
|
||||||
|
if remain < 0 {
|
||||||
|
if pos, err = f.Seek(0, io.SeekStart); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nread = bsize + remain
|
||||||
|
eof = true
|
||||||
|
} else {
|
||||||
|
if pos, err = f.Seek(offset, io.SeekEnd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = io.CopyN(buf, f, nread); err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b := buf.Bytes()
|
||||||
|
idx, done := lastIndexLines(b, &line, include)
|
||||||
|
|
||||||
|
if done {
|
||||||
|
if chunk == -1 {
|
||||||
|
// We found all N lines in the last chunk of the file.
|
||||||
|
// The seek offset is also now at the current end of file.
|
||||||
|
// Save this buffer to avoid another GET request when Read() is called.
|
||||||
|
buf.Next(int(idx + 1))
|
||||||
|
f.buf = buf
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = f.Seek(pos+idx+1, io.SeekStart); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if eof {
|
||||||
|
if remain < 0 {
|
||||||
|
// We found < N lines in the entire file, so seek to the start.
|
||||||
|
_, _ = f.Seek(0, io.SeekStart)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk--
|
||||||
|
buf.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type followDatastoreFile struct {
|
||||||
|
r *DatastoreFile
|
||||||
|
c chan struct{}
|
||||||
|
i time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads up to len(b) bytes from the DatastoreFile being followed.
|
||||||
|
// This method will block until data is read, an error other than io.EOF is returned or Close() is called.
|
||||||
|
func (f *followDatastoreFile) Read(p []byte) (int, error) {
|
||||||
|
offset := f.r.offset.seek
|
||||||
|
stop := false
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := f.r.Read(p)
|
||||||
|
if err != nil && err == io.EOF {
|
||||||
|
_ = f.r.Close() // GET request body has been drained.
|
||||||
|
if stop {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > 0 {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-f.c:
|
||||||
|
// Wake up and stop polling once the body has been drained
|
||||||
|
stop = true
|
||||||
|
case <-time.After(f.i):
|
||||||
|
}
|
||||||
|
|
||||||
|
info, serr := f.r.Stat()
|
||||||
|
if serr != nil {
|
||||||
|
// Return EOF rather than 404 if the file goes away
|
||||||
|
if serr == os.ErrNotExist {
|
||||||
|
_ = f.r.Close()
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return 0, serr
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Size() < offset {
|
||||||
|
// assume file has be truncated
|
||||||
|
offset, err = f.r.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close will stop Follow polling and close the underlying DatastoreFile.
|
||||||
|
func (f *followDatastoreFile) Close() error {
|
||||||
|
close(f.c)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Follow returns an io.ReadCloser to stream the file contents as data is appended.
|
||||||
|
func (f *DatastoreFile) Follow(interval time.Duration) io.ReadCloser {
|
||||||
|
return &followDatastoreFile{f, make(chan struct{}), interval}
|
||||||
|
}
|
145
vendor/github.com/vmware/govmomi/object/datastore_file_manager.go
generated
vendored
Normal file
145
vendor/github.com/vmware/govmomi/object/datastore_file_manager.go
generated
vendored
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 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 (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DatastoreFileManager combines FileManager and VirtualDiskManager to manage files on a Datastore
|
||||||
|
type DatastoreFileManager struct {
|
||||||
|
Datacenter *Datacenter
|
||||||
|
Datastore *Datastore
|
||||||
|
FileManager *FileManager
|
||||||
|
VirtualDiskManager *VirtualDiskManager
|
||||||
|
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileManager creates a new instance of DatastoreFileManager
|
||||||
|
func (d Datastore) NewFileManager(dc *Datacenter, force bool) *DatastoreFileManager {
|
||||||
|
c := d.Client()
|
||||||
|
|
||||||
|
m := &DatastoreFileManager{
|
||||||
|
Datacenter: dc,
|
||||||
|
Datastore: &d,
|
||||||
|
FileManager: NewFileManager(c),
|
||||||
|
VirtualDiskManager: NewVirtualDiskManager(c),
|
||||||
|
Force: force,
|
||||||
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete dispatches to the appropriate Delete method based on file name extension
|
||||||
|
func (m *DatastoreFileManager) Delete(ctx context.Context, name string) error {
|
||||||
|
switch path.Ext(name) {
|
||||||
|
case ".vmdk":
|
||||||
|
return m.DeleteVirtualDisk(ctx, name)
|
||||||
|
default:
|
||||||
|
return m.DeleteFile(ctx, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFile calls FileManager.DeleteDatastoreFile
|
||||||
|
func (m *DatastoreFileManager) DeleteFile(ctx context.Context, name string) error {
|
||||||
|
p := m.Path(name)
|
||||||
|
|
||||||
|
task, err := m.FileManager.DeleteDatastoreFile(ctx, p.String(), m.Datacenter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.Wait(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteVirtualDisk calls VirtualDiskManager.DeleteVirtualDisk
|
||||||
|
// Regardless of the Datastore type, DeleteVirtualDisk will fail if 'ddb.deletable=false',
|
||||||
|
// so if Force=true this method attempts to set 'ddb.deletable=true' before starting the delete task.
|
||||||
|
func (m *DatastoreFileManager) DeleteVirtualDisk(ctx context.Context, name string) error {
|
||||||
|
p := m.Path(name)
|
||||||
|
|
||||||
|
var merr error
|
||||||
|
|
||||||
|
if m.Force {
|
||||||
|
merr = m.markDiskAsDeletable(ctx, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
task, err := m.VirtualDiskManager.DeleteVirtualDisk(ctx, p.String(), m.Datacenter)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("markDiskAsDeletable(%s): %s", p, merr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.Wait(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path converts path name to a DatastorePath
|
||||||
|
func (m *DatastoreFileManager) Path(name string) *DatastorePath {
|
||||||
|
var p DatastorePath
|
||||||
|
|
||||||
|
if !p.FromString(name) {
|
||||||
|
p.Path = name
|
||||||
|
p.Datastore = m.Datastore.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DatastoreFileManager) markDiskAsDeletable(ctx context.Context, path *DatastorePath) error {
|
||||||
|
r, _, err := m.Datastore.Download(ctx, path.Path, &soap.DefaultDownload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
hasFlag := false
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
s := bufio.NewScanner(&io.LimitedReader{R: r, N: 2048}) // should be only a few hundred bytes, limit to be sure
|
||||||
|
|
||||||
|
for s.Scan() {
|
||||||
|
line := s.Text()
|
||||||
|
if strings.HasPrefix(line, "ddb.deletable") {
|
||||||
|
hasFlag = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(buf, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return err // any error other than EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasFlag {
|
||||||
|
return nil // already deletable, so leave as-is
|
||||||
|
}
|
||||||
|
|
||||||
|
// rewrite the .vmdk with ddb.deletable flag removed (the default is true)
|
||||||
|
return m.Datastore.Upload(ctx, buf, path.Path, &soap.DefaultUpload)
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 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 (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DatastorePath contains the components of a datastore path.
|
||||||
|
type DatastorePath struct {
|
||||||
|
Datastore string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromString parses a datastore path.
|
||||||
|
// Returns true if the path could be parsed, false otherwise.
|
||||||
|
func (p *DatastorePath) FromString(s string) bool {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
|
||||||
|
if !strings.HasPrefix(s, "[") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s = s[1:]
|
||||||
|
|
||||||
|
ix := strings.Index(s, "]")
|
||||||
|
if ix < 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Datastore = s[:ix]
|
||||||
|
p.Path = strings.TrimSpace(s[ix+1:])
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// String formats a datastore path.
|
||||||
|
func (p *DatastorePath) String() string {
|
||||||
|
s := fmt.Sprintf("[%s]", p.Datastore)
|
||||||
|
|
||||||
|
if p.Path == "" {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join([]string{s, p.Path}, " ")
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DiagnosticLog wraps DiagnosticManager.BrowseLog
|
||||||
|
type DiagnosticLog struct {
|
||||||
|
m DiagnosticManager
|
||||||
|
|
||||||
|
Key string
|
||||||
|
Host *HostSystem
|
||||||
|
|
||||||
|
Start int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek to log position starting at the last nlines of the log
|
||||||
|
func (l *DiagnosticLog) Seek(ctx context.Context, nlines int32) error {
|
||||||
|
h, err := l.m.BrowseLog(ctx, l.Host, l.Key, math.MaxInt32, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Start = h.LineEnd - nlines
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy log starting from l.Start to the given io.Writer
|
||||||
|
// Returns on error or when end of log is reached.
|
||||||
|
func (l *DiagnosticLog) Copy(ctx context.Context, w io.Writer) (int, error) {
|
||||||
|
const max = 500 // VC max == 500, ESX max == 1000
|
||||||
|
written := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
h, err := l.m.BrowseLog(ctx, l.Host, l.Key, l.Start, max)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range h.LineText {
|
||||||
|
n, err := fmt.Fprintln(w, line)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Start += int32(len(h.LineText))
|
||||||
|
|
||||||
|
if l.Start >= h.LineEnd {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return written, nil
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DiagnosticManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDiagnosticManager(c *vim25.Client) *DiagnosticManager {
|
||||||
|
m := DiagnosticManager{
|
||||||
|
Common: NewCommon(c, *c.ServiceContent.DiagnosticManager),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m DiagnosticManager) Log(ctx context.Context, host *HostSystem, key string) *DiagnosticLog {
|
||||||
|
return &DiagnosticLog{
|
||||||
|
m: m,
|
||||||
|
Key: key,
|
||||||
|
Host: host,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m DiagnosticManager) BrowseLog(ctx context.Context, host *HostSystem, key string, start, lines int32) (*types.DiagnosticManagerLogHeader, error) {
|
||||||
|
req := types.BrowseDiagnosticLog{
|
||||||
|
This: m.Reference(),
|
||||||
|
Key: key,
|
||||||
|
Start: start,
|
||||||
|
Lines: lines,
|
||||||
|
}
|
||||||
|
|
||||||
|
if host != nil {
|
||||||
|
ref := host.Reference()
|
||||||
|
req.Host = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.BrowseDiagnosticLog(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m DiagnosticManager) GenerateLogBundles(ctx context.Context, includeDefault bool, host []*HostSystem) (*Task, error) {
|
||||||
|
req := types.GenerateLogBundles_Task{
|
||||||
|
This: m.Reference(),
|
||||||
|
IncludeDefault: includeDefault,
|
||||||
|
}
|
||||||
|
|
||||||
|
if host != nil {
|
||||||
|
for _, h := range host {
|
||||||
|
req.Host = append(req.Host, h.Reference())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.GenerateLogBundles_Task(ctx, m.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(m.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m DiagnosticManager) QueryDescriptions(ctx context.Context, host *HostSystem) ([]types.DiagnosticManagerLogDescriptor, error) {
|
||||||
|
req := types.QueryDescriptions{
|
||||||
|
This: m.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if host != nil {
|
||||||
|
ref := host.Reference()
|
||||||
|
req.Host = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.QueryDescriptions(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
73
vendor/github.com/vmware/govmomi/object/distributed_virtual_portgroup.go
generated
vendored
Normal file
73
vendor/github.com/vmware/govmomi/object/distributed_virtual_portgroup.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DistributedVirtualPortgroup struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDistributedVirtualPortgroup(c *vim25.Client, ref types.ManagedObjectReference) *DistributedVirtualPortgroup {
|
||||||
|
return &DistributedVirtualPortgroup{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EthernetCardBackingInfo returns the VirtualDeviceBackingInfo for this DistributedVirtualPortgroup
|
||||||
|
func (p DistributedVirtualPortgroup) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
|
||||||
|
var dvp mo.DistributedVirtualPortgroup
|
||||||
|
var dvs mo.VmwareDistributedVirtualSwitch // TODO: should be mo.BaseDistributedVirtualSwitch
|
||||||
|
|
||||||
|
if err := p.Properties(ctx, p.Reference(), []string{"key", "config.distributedVirtualSwitch"}, &dvp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.Properties(ctx, *dvp.Config.DistributedVirtualSwitch, []string{"uuid"}, &dvs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
backing := &types.VirtualEthernetCardDistributedVirtualPortBackingInfo{
|
||||||
|
Port: types.DistributedVirtualSwitchPortConnection{
|
||||||
|
PortgroupKey: dvp.Key,
|
||||||
|
SwitchUuid: dvs.Uuid,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return backing, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p DistributedVirtualPortgroup) Reconfigure(ctx context.Context, spec types.DVPortgroupConfigSpec) (*Task, error) {
|
||||||
|
req := types.ReconfigureDVPortgroup_Task{
|
||||||
|
This: p.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ReconfigureDVPortgroup_Task(ctx, p.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(p.Client(), res.Returnval), nil
|
||||||
|
}
|
67
vendor/github.com/vmware/govmomi/object/distributed_virtual_switch.go
generated
vendored
Normal file
67
vendor/github.com/vmware/govmomi/object/distributed_virtual_switch.go
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DistributedVirtualSwitch struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDistributedVirtualSwitch(c *vim25.Client, ref types.ManagedObjectReference) *DistributedVirtualSwitch {
|
||||||
|
return &DistributedVirtualSwitch{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s DistributedVirtualSwitch) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
|
||||||
|
return nil, ErrNotSupported // TODO: just to satisfy NetworkReference interface for the finder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s DistributedVirtualSwitch) Reconfigure(ctx context.Context, spec types.BaseDVSConfigSpec) (*Task, error) {
|
||||||
|
req := types.ReconfigureDvs_Task{
|
||||||
|
This: s.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ReconfigureDvs_Task(ctx, s.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(s.Client(), res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s DistributedVirtualSwitch) AddPortgroup(ctx context.Context, spec []types.DVPortgroupConfigSpec) (*Task, error) {
|
||||||
|
req := types.AddDVPortgroup_Task{
|
||||||
|
This: s.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.AddDVPortgroup_Task(ctx, s.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(s.Client(), res.Returnval), nil
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExtensionManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExtensionManager wraps NewExtensionManager, returning ErrNotSupported
|
||||||
|
// when the client is not connected to a vCenter instance.
|
||||||
|
func GetExtensionManager(c *vim25.Client) (*ExtensionManager, error) {
|
||||||
|
if c.ServiceContent.ExtensionManager == nil {
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
return NewExtensionManager(c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExtensionManager(c *vim25.Client) *ExtensionManager {
|
||||||
|
o := ExtensionManager{
|
||||||
|
Common: NewCommon(c, *c.ServiceContent.ExtensionManager),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ExtensionManager) List(ctx context.Context) ([]types.Extension, error) {
|
||||||
|
var em mo.ExtensionManager
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"extensionList"}, &em)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return em.ExtensionList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ExtensionManager) Find(ctx context.Context, key string) (*types.Extension, error) {
|
||||||
|
req := types.FindExtension{
|
||||||
|
This: m.Reference(),
|
||||||
|
ExtensionKey: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.FindExtension(ctx, m.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ExtensionManager) Register(ctx context.Context, extension types.Extension) error {
|
||||||
|
req := types.RegisterExtension{
|
||||||
|
This: m.Reference(),
|
||||||
|
Extension: extension,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RegisterExtension(ctx, m.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ExtensionManager) SetCertificate(ctx context.Context, key string, certificatePem string) error {
|
||||||
|
req := types.SetExtensionCertificate{
|
||||||
|
This: m.Reference(),
|
||||||
|
ExtensionKey: key,
|
||||||
|
CertificatePem: certificatePem,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.SetExtensionCertificate(ctx, m.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ExtensionManager) Unregister(ctx context.Context, key string) error {
|
||||||
|
req := types.UnregisterExtension{
|
||||||
|
This: m.Reference(),
|
||||||
|
ExtensionKey: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UnregisterExtension(ctx, m.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ExtensionManager) Update(ctx context.Context, extension types.Extension) error {
|
||||||
|
req := types.UpdateExtension{
|
||||||
|
This: m.Reference(),
|
||||||
|
Extension: extension,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateExtension(ctx, m.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFileManager(c *vim25.Client) *FileManager {
|
||||||
|
f := FileManager{
|
||||||
|
Common: NewCommon(c, *c.ServiceContent.FileManager),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileManager) CopyDatastoreFile(ctx context.Context, sourceName string, sourceDatacenter *Datacenter, destinationName string, destinationDatacenter *Datacenter, force bool) (*Task, error) {
|
||||||
|
req := types.CopyDatastoreFile_Task{
|
||||||
|
This: f.Reference(),
|
||||||
|
SourceName: sourceName,
|
||||||
|
DestinationName: destinationName,
|
||||||
|
Force: types.NewBool(force),
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceDatacenter != nil {
|
||||||
|
ref := sourceDatacenter.Reference()
|
||||||
|
req.SourceDatacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
if destinationDatacenter != nil {
|
||||||
|
ref := destinationDatacenter.Reference()
|
||||||
|
req.DestinationDatacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CopyDatastoreFile_Task(ctx, f.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(f.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteDatastoreFile deletes the specified file or folder from the datastore.
|
||||||
|
func (f FileManager) DeleteDatastoreFile(ctx context.Context, name string, dc *Datacenter) (*Task, error) {
|
||||||
|
req := types.DeleteDatastoreFile_Task{
|
||||||
|
This: f.Reference(),
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
if dc != nil {
|
||||||
|
ref := dc.Reference()
|
||||||
|
req.Datacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.DeleteDatastoreFile_Task(ctx, f.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(f.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeDirectory creates a folder using the specified name.
|
||||||
|
func (f FileManager) MakeDirectory(ctx context.Context, name string, dc *Datacenter, createParentDirectories bool) error {
|
||||||
|
req := types.MakeDirectory{
|
||||||
|
This: f.Reference(),
|
||||||
|
Name: name,
|
||||||
|
CreateParentDirectories: types.NewBool(createParentDirectories),
|
||||||
|
}
|
||||||
|
|
||||||
|
if dc != nil {
|
||||||
|
ref := dc.Reference()
|
||||||
|
req.Datacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.MakeDirectory(ctx, f.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileManager) MoveDatastoreFile(ctx context.Context, sourceName string, sourceDatacenter *Datacenter, destinationName string, destinationDatacenter *Datacenter, force bool) (*Task, error) {
|
||||||
|
req := types.MoveDatastoreFile_Task{
|
||||||
|
This: f.Reference(),
|
||||||
|
SourceName: sourceName,
|
||||||
|
DestinationName: destinationName,
|
||||||
|
Force: types.NewBool(force),
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceDatacenter != nil {
|
||||||
|
ref := sourceDatacenter.Reference()
|
||||||
|
req.SourceDatacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
if destinationDatacenter != nil {
|
||||||
|
ref := destinationDatacenter.Reference()
|
||||||
|
req.DestinationDatacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.MoveDatastoreFile_Task(ctx, f.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(f.c, res.Returnval), nil
|
||||||
|
}
|
|
@ -0,0 +1,227 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Folder struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFolder(c *vim25.Client, ref types.ManagedObjectReference) *Folder {
|
||||||
|
return &Folder{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRootFolder(c *vim25.Client) *Folder {
|
||||||
|
f := NewFolder(c, c.ServiceContent.RootFolder)
|
||||||
|
f.InventoryPath = "/"
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Folder) Children(ctx context.Context) ([]Reference, error) {
|
||||||
|
var mf mo.Folder
|
||||||
|
|
||||||
|
err := f.Properties(ctx, f.Reference(), []string{"childEntity"}, &mf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var rs []Reference
|
||||||
|
for _, e := range mf.ChildEntity {
|
||||||
|
if r := NewReference(f.c, e); r != nil {
|
||||||
|
rs = append(rs, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Folder) CreateDatacenter(ctx context.Context, datacenter string) (*Datacenter, error) {
|
||||||
|
req := types.CreateDatacenter{
|
||||||
|
This: f.Reference(),
|
||||||
|
Name: datacenter,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateDatacenter(ctx, f.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response will be nil if this is an ESX host that does not belong to a vCenter
|
||||||
|
if res == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewDatacenter(f.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Folder) CreateCluster(ctx context.Context, cluster string, spec types.ClusterConfigSpecEx) (*ClusterComputeResource, error) {
|
||||||
|
req := types.CreateClusterEx{
|
||||||
|
This: f.Reference(),
|
||||||
|
Name: cluster,
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateClusterEx(ctx, f.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response will be nil if this is an ESX host that does not belong to a vCenter
|
||||||
|
if res == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewClusterComputeResource(f.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Folder) CreateFolder(ctx context.Context, name string) (*Folder, error) {
|
||||||
|
req := types.CreateFolder{
|
||||||
|
This: f.Reference(),
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateFolder(ctx, f.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewFolder(f.c, res.Returnval), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Folder) CreateStoragePod(ctx context.Context, name string) (*StoragePod, error) {
|
||||||
|
req := types.CreateStoragePod{
|
||||||
|
This: f.Reference(),
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateStoragePod(ctx, f.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewStoragePod(f.c, res.Returnval), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Folder) AddStandaloneHost(ctx context.Context, spec types.HostConnectSpec, addConnected bool, license *string, compResSpec *types.BaseComputeResourceConfigSpec) (*Task, error) {
|
||||||
|
req := types.AddStandaloneHost_Task{
|
||||||
|
This: f.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
AddConnected: addConnected,
|
||||||
|
}
|
||||||
|
|
||||||
|
if license != nil {
|
||||||
|
req.License = *license
|
||||||
|
}
|
||||||
|
|
||||||
|
if compResSpec != nil {
|
||||||
|
req.CompResSpec = *compResSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.AddStandaloneHost_Task(ctx, f.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(f.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Folder) CreateVM(ctx context.Context, config types.VirtualMachineConfigSpec, pool *ResourcePool, host *HostSystem) (*Task, error) {
|
||||||
|
req := types.CreateVM_Task{
|
||||||
|
This: f.Reference(),
|
||||||
|
Config: config,
|
||||||
|
Pool: pool.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if host != nil {
|
||||||
|
ref := host.Reference()
|
||||||
|
req.Host = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateVM_Task(ctx, f.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(f.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Folder) RegisterVM(ctx context.Context, path string, name string, asTemplate bool, pool *ResourcePool, host *HostSystem) (*Task, error) {
|
||||||
|
req := types.RegisterVM_Task{
|
||||||
|
This: f.Reference(),
|
||||||
|
Path: path,
|
||||||
|
AsTemplate: asTemplate,
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != "" {
|
||||||
|
req.Name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
if host != nil {
|
||||||
|
ref := host.Reference()
|
||||||
|
req.Host = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
if pool != nil {
|
||||||
|
ref := pool.Reference()
|
||||||
|
req.Pool = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.RegisterVM_Task(ctx, f.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(f.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Folder) CreateDVS(ctx context.Context, spec types.DVSCreateSpec) (*Task, error) {
|
||||||
|
req := types.CreateDVS_Task{
|
||||||
|
This: f.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateDVS_Task(ctx, f.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(f.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Folder) MoveInto(ctx context.Context, list []types.ManagedObjectReference) (*Task, error) {
|
||||||
|
req := types.MoveIntoFolder_Task{
|
||||||
|
This: f.Reference(),
|
||||||
|
List: list,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.MoveIntoFolder_Task(ctx, f.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(f.c, res.Returnval), nil
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HistoryCollector struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHistoryCollector(c *vim25.Client, ref types.ManagedObjectReference) *HistoryCollector {
|
||||||
|
return &HistoryCollector{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HistoryCollector) Destroy(ctx context.Context) error {
|
||||||
|
req := types.DestroyCollector{
|
||||||
|
This: h.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.DestroyCollector(ctx, h.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HistoryCollector) Reset(ctx context.Context) error {
|
||||||
|
req := types.ResetCollector{
|
||||||
|
This: h.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.ResetCollector(ctx, h.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HistoryCollector) Rewind(ctx context.Context) error {
|
||||||
|
req := types.RewindCollector{
|
||||||
|
This: h.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RewindCollector(ctx, h.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HistoryCollector) SetPageSize(ctx context.Context, maxCount int32) error {
|
||||||
|
req := types.SetCollectorPageSize{
|
||||||
|
This: h.Reference(),
|
||||||
|
MaxCount: maxCount,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.SetCollectorPageSize(ctx, h.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostAccountManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostAccountManager(c *vim25.Client, ref types.ManagedObjectReference) *HostAccountManager {
|
||||||
|
return &HostAccountManager{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostAccountManager) Create(ctx context.Context, user *types.HostAccountSpec) error {
|
||||||
|
req := types.CreateUser{
|
||||||
|
This: m.Reference(),
|
||||||
|
User: user,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.CreateUser(ctx, m.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostAccountManager) Update(ctx context.Context, user *types.HostAccountSpec) error {
|
||||||
|
req := types.UpdateUser{
|
||||||
|
This: m.Reference(),
|
||||||
|
User: user,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateUser(ctx, m.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostAccountManager) Remove(ctx context.Context, userName string) error {
|
||||||
|
req := types.RemoveUser{
|
||||||
|
This: m.Reference(),
|
||||||
|
UserName: userName,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RemoveUser(ctx, m.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 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 (
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HostCertificateInfo provides helpers for types.HostCertificateManagerCertificateInfo
|
||||||
|
type HostCertificateInfo struct {
|
||||||
|
types.HostCertificateManagerCertificateInfo
|
||||||
|
|
||||||
|
ThumbprintSHA1 string
|
||||||
|
ThumbprintSHA256 string
|
||||||
|
|
||||||
|
Err error
|
||||||
|
Certificate *x509.Certificate `json:"-"`
|
||||||
|
|
||||||
|
subjectName *pkix.Name
|
||||||
|
issuerName *pkix.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromCertificate converts x509.Certificate to HostCertificateInfo
|
||||||
|
func (info *HostCertificateInfo) FromCertificate(cert *x509.Certificate) *HostCertificateInfo {
|
||||||
|
info.Certificate = cert
|
||||||
|
info.subjectName = &cert.Subject
|
||||||
|
info.issuerName = &cert.Issuer
|
||||||
|
|
||||||
|
info.Issuer = info.fromName(info.issuerName)
|
||||||
|
info.NotBefore = &cert.NotBefore
|
||||||
|
info.NotAfter = &cert.NotAfter
|
||||||
|
info.Subject = info.fromName(info.subjectName)
|
||||||
|
|
||||||
|
info.ThumbprintSHA1 = soap.ThumbprintSHA1(cert)
|
||||||
|
|
||||||
|
// SHA-256 for info purposes only, API fields all use SHA-1
|
||||||
|
sum := sha256.Sum256(cert.Raw)
|
||||||
|
hex := make([]string, len(sum))
|
||||||
|
for i, b := range sum {
|
||||||
|
hex[i] = fmt.Sprintf("%02X", b)
|
||||||
|
}
|
||||||
|
info.ThumbprintSHA256 = strings.Join(hex, ":")
|
||||||
|
|
||||||
|
if info.Status == "" {
|
||||||
|
info.Status = string(types.HostCertificateManagerCertificateInfoCertificateStatusUnknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromURL connects to the given URL.Host via tls.Dial with the given tls.Config and populates the HostCertificateInfo
|
||||||
|
// via tls.ConnectionState. If the certificate was verified with the given tls.Config, the Err field will be nil.
|
||||||
|
// Otherwise, Err will be set to the x509.UnknownAuthorityError or x509.HostnameError.
|
||||||
|
// If tls.Dial returns an error of any other type, that error is returned.
|
||||||
|
func (info *HostCertificateInfo) FromURL(u *url.URL, config *tls.Config) error {
|
||||||
|
addr := u.Host
|
||||||
|
if !(strings.LastIndex(addr, ":") > strings.LastIndex(addr, "]")) {
|
||||||
|
addr += ":443"
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := tls.Dial("tcp", addr, config)
|
||||||
|
if err != nil {
|
||||||
|
switch err.(type) {
|
||||||
|
case x509.UnknownAuthorityError:
|
||||||
|
case x509.HostnameError:
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info.Err = err
|
||||||
|
|
||||||
|
conn, err = tls.Dial("tcp", addr, &tls.Config{InsecureSkipVerify: true})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info.Status = string(types.HostCertificateManagerCertificateInfoCertificateStatusGood)
|
||||||
|
}
|
||||||
|
|
||||||
|
state := conn.ConnectionState()
|
||||||
|
_ = conn.Close()
|
||||||
|
info.FromCertificate(state.PeerCertificates[0])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var emailAddressOID = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}
|
||||||
|
|
||||||
|
func (info *HostCertificateInfo) fromName(name *pkix.Name) string {
|
||||||
|
var attrs []string
|
||||||
|
|
||||||
|
oids := map[string]string{
|
||||||
|
emailAddressOID.String(): "emailAddress",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, attr := range name.Names {
|
||||||
|
if key, ok := oids[attr.Type.String()]; ok {
|
||||||
|
attrs = append(attrs, fmt.Sprintf("%s=%s", key, attr.Value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs = append(attrs, fmt.Sprintf("CN=%s", name.CommonName))
|
||||||
|
|
||||||
|
add := func(key string, vals []string) {
|
||||||
|
for _, val := range vals {
|
||||||
|
attrs = append(attrs, fmt.Sprintf("%s=%s", key, val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elts := []struct {
|
||||||
|
key string
|
||||||
|
val []string
|
||||||
|
}{
|
||||||
|
{"OU", name.OrganizationalUnit},
|
||||||
|
{"O", name.Organization},
|
||||||
|
{"L", name.Locality},
|
||||||
|
{"ST", name.Province},
|
||||||
|
{"C", name.Country},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, elt := range elts {
|
||||||
|
add(elt.key, elt.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(attrs, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *HostCertificateInfo) toName(s string) *pkix.Name {
|
||||||
|
var name pkix.Name
|
||||||
|
|
||||||
|
for _, pair := range strings.Split(s, ",") {
|
||||||
|
attr := strings.SplitN(pair, "=", 2)
|
||||||
|
if len(attr) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
v := attr[1]
|
||||||
|
|
||||||
|
switch strings.ToLower(attr[0]) {
|
||||||
|
case "cn":
|
||||||
|
name.CommonName = v
|
||||||
|
case "ou":
|
||||||
|
name.OrganizationalUnit = append(name.OrganizationalUnit, v)
|
||||||
|
case "o":
|
||||||
|
name.Organization = append(name.Organization, v)
|
||||||
|
case "l":
|
||||||
|
name.Locality = append(name.Locality, v)
|
||||||
|
case "st":
|
||||||
|
name.Province = append(name.Province, v)
|
||||||
|
case "c":
|
||||||
|
name.Country = append(name.Country, v)
|
||||||
|
case "emailaddress":
|
||||||
|
name.Names = append(name.Names, pkix.AttributeTypeAndValue{Type: emailAddressOID, Value: v})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &name
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubjectName parses Subject into a pkix.Name
|
||||||
|
func (info *HostCertificateInfo) SubjectName() *pkix.Name {
|
||||||
|
if info.subjectName != nil {
|
||||||
|
return info.subjectName
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.toName(info.Subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssuerName parses Issuer into a pkix.Name
|
||||||
|
func (info *HostCertificateInfo) IssuerName() *pkix.Name {
|
||||||
|
if info.issuerName != nil {
|
||||||
|
return info.issuerName
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.toName(info.Issuer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write outputs info similar to the Chrome Certificate Viewer.
|
||||||
|
func (info *HostCertificateInfo) Write(w io.Writer) error {
|
||||||
|
tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)
|
||||||
|
|
||||||
|
s := func(val string) string {
|
||||||
|
if val != "" {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return "<Not Part Of Certificate>"
|
||||||
|
}
|
||||||
|
|
||||||
|
ss := func(val []string) string {
|
||||||
|
return s(strings.Join(val, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
name := func(n *pkix.Name) {
|
||||||
|
fmt.Fprintf(tw, " Common Name (CN):\t%s\n", s(n.CommonName))
|
||||||
|
fmt.Fprintf(tw, " Organization (O):\t%s\n", ss(n.Organization))
|
||||||
|
fmt.Fprintf(tw, " Organizational Unit (OU):\t%s\n", ss(n.OrganizationalUnit))
|
||||||
|
}
|
||||||
|
|
||||||
|
status := info.Status
|
||||||
|
if info.Err != nil {
|
||||||
|
status = fmt.Sprintf("ERROR %s", info.Err)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(tw, "Certificate Status:\t%s\n", status)
|
||||||
|
|
||||||
|
fmt.Fprintln(tw, "Issued To:\t")
|
||||||
|
name(info.SubjectName())
|
||||||
|
|
||||||
|
fmt.Fprintln(tw, "Issued By:\t")
|
||||||
|
name(info.IssuerName())
|
||||||
|
|
||||||
|
fmt.Fprintln(tw, "Validity Period:\t")
|
||||||
|
fmt.Fprintf(tw, " Issued On:\t%s\n", info.NotBefore)
|
||||||
|
fmt.Fprintf(tw, " Expires On:\t%s\n", info.NotAfter)
|
||||||
|
|
||||||
|
if info.ThumbprintSHA1 != "" {
|
||||||
|
fmt.Fprintln(tw, "Thumbprints:\t")
|
||||||
|
if info.ThumbprintSHA256 != "" {
|
||||||
|
fmt.Fprintf(tw, " SHA-256 Thumbprint:\t%s\n", info.ThumbprintSHA256)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(tw, " SHA-1 Thumbprint:\t%s\n", info.ThumbprintSHA1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tw.Flush()
|
||||||
|
}
|
162
vendor/github.com/vmware/govmomi/object/host_certificate_manager.go
generated
vendored
Normal file
162
vendor/github.com/vmware/govmomi/object/host_certificate_manager.go
generated
vendored
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 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/property"
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HostCertificateManager provides helper methods around the HostSystem.ConfigManager.CertificateManager
|
||||||
|
type HostCertificateManager struct {
|
||||||
|
Common
|
||||||
|
Host *HostSystem
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHostCertificateManager creates a new HostCertificateManager helper
|
||||||
|
func NewHostCertificateManager(c *vim25.Client, ref types.ManagedObjectReference, host types.ManagedObjectReference) *HostCertificateManager {
|
||||||
|
return &HostCertificateManager{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
Host: NewHostSystem(c, host),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateInfo wraps the host CertificateManager certificateInfo property with the HostCertificateInfo helper.
|
||||||
|
// The ThumbprintSHA1 field is set to HostSystem.Summary.Config.SslThumbprint if the host system is managed by a vCenter.
|
||||||
|
func (m HostCertificateManager) CertificateInfo(ctx context.Context) (*HostCertificateInfo, error) {
|
||||||
|
var hs mo.HostSystem
|
||||||
|
var cm mo.HostCertificateManager
|
||||||
|
|
||||||
|
pc := property.DefaultCollector(m.Client())
|
||||||
|
|
||||||
|
err := pc.RetrieveOne(ctx, m.Reference(), []string{"certificateInfo"}, &cm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = pc.RetrieveOne(ctx, m.Host.Reference(), []string{"summary.config.sslThumbprint"}, &hs)
|
||||||
|
|
||||||
|
return &HostCertificateInfo{
|
||||||
|
HostCertificateManagerCertificateInfo: cm.CertificateInfo,
|
||||||
|
ThumbprintSHA1: hs.Summary.Config.SslThumbprint,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateCertificateSigningRequest requests the host system to generate a certificate-signing request (CSR) for itself.
|
||||||
|
// The CSR is then typically provided to a Certificate Authority to sign and issue the SSL certificate for the host system.
|
||||||
|
// Use InstallServerCertificate to import this certificate.
|
||||||
|
func (m HostCertificateManager) GenerateCertificateSigningRequest(ctx context.Context, useIPAddressAsCommonName bool) (string, error) {
|
||||||
|
req := types.GenerateCertificateSigningRequest{
|
||||||
|
This: m.Reference(),
|
||||||
|
UseIpAddressAsCommonName: useIPAddressAsCommonName,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.GenerateCertificateSigningRequest(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateCertificateSigningRequestByDn requests the host system to generate a certificate-signing request (CSR) for itself.
|
||||||
|
// Alternative version similar to GenerateCertificateSigningRequest but takes a Distinguished Name (DN) as a parameter.
|
||||||
|
func (m HostCertificateManager) GenerateCertificateSigningRequestByDn(ctx context.Context, distinguishedName string) (string, error) {
|
||||||
|
req := types.GenerateCertificateSigningRequestByDn{
|
||||||
|
This: m.Reference(),
|
||||||
|
DistinguishedName: distinguishedName,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.GenerateCertificateSigningRequestByDn(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstallServerCertificate imports the given SSL certificate to the host system.
|
||||||
|
func (m HostCertificateManager) InstallServerCertificate(ctx context.Context, cert string) error {
|
||||||
|
req := types.InstallServerCertificate{
|
||||||
|
This: m.Reference(),
|
||||||
|
Cert: cert,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.InstallServerCertificate(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyAffectedService is internal, not exposing as we don't have a use case other than with InstallServerCertificate
|
||||||
|
// Without this call, hostd needs to be restarted to use the updated certificate
|
||||||
|
// Note: using Refresh as it has the same struct/signature, we just need to use different xml name tags
|
||||||
|
body := struct {
|
||||||
|
Req *types.Refresh `xml:"urn:vim25 NotifyAffectedServices,omitempty"`
|
||||||
|
Res *types.RefreshResponse `xml:"urn:vim25 NotifyAffectedServicesResponse,omitempty"`
|
||||||
|
methods.RefreshBody
|
||||||
|
}{
|
||||||
|
Req: &types.Refresh{This: m.Reference()},
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.Client().RoundTrip(ctx, &body, &body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListCACertificateRevocationLists returns the SSL CRLs of Certificate Authorities that are trusted by the host system.
|
||||||
|
func (m HostCertificateManager) ListCACertificateRevocationLists(ctx context.Context) ([]string, error) {
|
||||||
|
req := types.ListCACertificateRevocationLists{
|
||||||
|
This: m.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ListCACertificateRevocationLists(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListCACertificates returns the SSL certificates of Certificate Authorities that are trusted by the host system.
|
||||||
|
func (m HostCertificateManager) ListCACertificates(ctx context.Context) ([]string, error) {
|
||||||
|
req := types.ListCACertificates{
|
||||||
|
This: m.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ListCACertificates(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceCACertificatesAndCRLs replaces the trusted CA certificates and CRL used by the host system.
|
||||||
|
// These determine whether the server can verify the identity of an external entity.
|
||||||
|
func (m HostCertificateManager) ReplaceCACertificatesAndCRLs(ctx context.Context, caCert []string, caCrl []string) error {
|
||||||
|
req := types.ReplaceCACertificatesAndCRLs{
|
||||||
|
This: m.Reference(),
|
||||||
|
CaCert: caCert,
|
||||||
|
CaCrl: caCrl,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.ReplaceCACertificatesAndCRLs(ctx, m.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostConfigManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostConfigManager(c *vim25.Client, ref types.ManagedObjectReference) *HostConfigManager {
|
||||||
|
return &HostConfigManager{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostConfigManager) DatastoreSystem(ctx context.Context) (*HostDatastoreSystem, error) {
|
||||||
|
var h mo.HostSystem
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"configManager.datastoreSystem"}, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostDatastoreSystem(m.c, *h.ConfigManager.DatastoreSystem), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostConfigManager) NetworkSystem(ctx context.Context) (*HostNetworkSystem, error) {
|
||||||
|
var h mo.HostSystem
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"configManager.networkSystem"}, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostNetworkSystem(m.c, *h.ConfigManager.NetworkSystem), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostConfigManager) FirewallSystem(ctx context.Context) (*HostFirewallSystem, error) {
|
||||||
|
var h mo.HostSystem
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"configManager.firewallSystem"}, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostFirewallSystem(m.c, *h.ConfigManager.FirewallSystem), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostConfigManager) StorageSystem(ctx context.Context) (*HostStorageSystem, error) {
|
||||||
|
var h mo.HostSystem
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"configManager.storageSystem"}, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostStorageSystem(m.c, *h.ConfigManager.StorageSystem), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostConfigManager) VirtualNicManager(ctx context.Context) (*HostVirtualNicManager, error) {
|
||||||
|
var h mo.HostSystem
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"configManager.virtualNicManager"}, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostVirtualNicManager(m.c, *h.ConfigManager.VirtualNicManager, m.Reference()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostConfigManager) VsanSystem(ctx context.Context) (*HostVsanSystem, error) {
|
||||||
|
var h mo.HostSystem
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"configManager.vsanSystem"}, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added in 5.5
|
||||||
|
if h.ConfigManager.VsanSystem == nil {
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostVsanSystem(m.c, *h.ConfigManager.VsanSystem), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostConfigManager) VsanInternalSystem(ctx context.Context) (*HostVsanInternalSystem, error) {
|
||||||
|
var h mo.HostSystem
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"configManager.vsanInternalSystem"}, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added in 5.5
|
||||||
|
if h.ConfigManager.VsanInternalSystem == nil {
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostVsanInternalSystem(m.c, *h.ConfigManager.VsanInternalSystem), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostConfigManager) AccountManager(ctx context.Context) (*HostAccountManager, error) {
|
||||||
|
var h mo.HostSystem
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"configManager.accountManager"}, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ref := h.ConfigManager.AccountManager // Added in 6.0
|
||||||
|
if ref == nil {
|
||||||
|
// Versions < 5.5 can use the ServiceContent ref,
|
||||||
|
// but we can only use it when connected directly to ESX.
|
||||||
|
c := m.Client()
|
||||||
|
if !c.IsVC() {
|
||||||
|
ref = c.ServiceContent.AccountManager
|
||||||
|
}
|
||||||
|
|
||||||
|
if ref == nil {
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostAccountManager(m.c, *ref), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostConfigManager) OptionManager(ctx context.Context) (*OptionManager, error) {
|
||||||
|
var h mo.HostSystem
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"configManager.advancedOption"}, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewOptionManager(m.c, *h.ConfigManager.AdvancedOption), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostConfigManager) ServiceSystem(ctx context.Context) (*HostServiceSystem, error) {
|
||||||
|
var h mo.HostSystem
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"configManager.serviceSystem"}, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostServiceSystem(m.c, *h.ConfigManager.ServiceSystem), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostConfigManager) CertificateManager(ctx context.Context) (*HostCertificateManager, error) {
|
||||||
|
var h mo.HostSystem
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"configManager.certificateManager"}, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added in 6.0
|
||||||
|
if h.ConfigManager.CertificateManager == nil {
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostCertificateManager(m.c, *h.ConfigManager.CertificateManager, m.Reference()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostConfigManager) DateTimeSystem(ctx context.Context) (*HostDateTimeSystem, error) {
|
||||||
|
var h mo.HostSystem
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"configManager.dateTimeSystem"}, &h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostDateTimeSystem(m.c, *h.ConfigManager.DateTimeSystem), nil
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostDatastoreBrowser struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostDatastoreBrowser(c *vim25.Client, ref types.ManagedObjectReference) *HostDatastoreBrowser {
|
||||||
|
return &HostDatastoreBrowser{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b HostDatastoreBrowser) SearchDatastore(ctx context.Context, datastorePath string, searchSpec *types.HostDatastoreBrowserSearchSpec) (*Task, error) {
|
||||||
|
req := types.SearchDatastore_Task{
|
||||||
|
This: b.Reference(),
|
||||||
|
DatastorePath: datastorePath,
|
||||||
|
SearchSpec: searchSpec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.SearchDatastore_Task(ctx, b.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(b.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b HostDatastoreBrowser) SearchDatastoreSubFolders(ctx context.Context, datastorePath string, searchSpec *types.HostDatastoreBrowserSearchSpec) (*Task, error) {
|
||||||
|
req := types.SearchDatastoreSubFolders_Task{
|
||||||
|
This: b.Reference(),
|
||||||
|
DatastorePath: datastorePath,
|
||||||
|
SearchSpec: searchSpec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.SearchDatastoreSubFolders_Task(ctx, b.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(b.c, res.Returnval), nil
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostDatastoreSystem struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostDatastoreSystem(c *vim25.Client, ref types.ManagedObjectReference) *HostDatastoreSystem {
|
||||||
|
return &HostDatastoreSystem{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostDatastoreSystem) CreateLocalDatastore(ctx context.Context, name string, path string) (*Datastore, error) {
|
||||||
|
req := types.CreateLocalDatastore{
|
||||||
|
This: s.Reference(),
|
||||||
|
Name: name,
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateLocalDatastore(ctx, s.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewDatastore(s.Client(), res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostDatastoreSystem) CreateNasDatastore(ctx context.Context, spec types.HostNasVolumeSpec) (*Datastore, error) {
|
||||||
|
req := types.CreateNasDatastore{
|
||||||
|
This: s.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateNasDatastore(ctx, s.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewDatastore(s.Client(), res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostDatastoreSystem) CreateVmfsDatastore(ctx context.Context, spec types.VmfsDatastoreCreateSpec) (*Datastore, error) {
|
||||||
|
req := types.CreateVmfsDatastore{
|
||||||
|
This: s.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateVmfsDatastore(ctx, s.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewDatastore(s.Client(), res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostDatastoreSystem) Remove(ctx context.Context, ds *Datastore) error {
|
||||||
|
req := types.RemoveDatastore{
|
||||||
|
This: s.Reference(),
|
||||||
|
Datastore: ds.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RemoveDatastore(ctx, s.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostDatastoreSystem) QueryAvailableDisksForVmfs(ctx context.Context) ([]types.HostScsiDisk, error) {
|
||||||
|
req := types.QueryAvailableDisksForVmfs{
|
||||||
|
This: s.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.QueryAvailableDisksForVmfs(ctx, s.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostDatastoreSystem) QueryVmfsDatastoreCreateOptions(ctx context.Context, devicePath string) ([]types.VmfsDatastoreOption, error) {
|
||||||
|
req := types.QueryVmfsDatastoreCreateOptions{
|
||||||
|
This: s.Reference(),
|
||||||
|
DevicePath: devicePath,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.QueryVmfsDatastoreCreateOptions(ctx, s.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 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"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostDateTimeSystem struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostDateTimeSystem(c *vim25.Client, ref types.ManagedObjectReference) *HostDateTimeSystem {
|
||||||
|
return &HostDateTimeSystem{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostDateTimeSystem) UpdateConfig(ctx context.Context, config types.HostDateTimeConfig) error {
|
||||||
|
req := types.UpdateDateTimeConfig{
|
||||||
|
This: s.Reference(),
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateDateTimeConfig(ctx, s.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostDateTimeSystem) Update(ctx context.Context, date time.Time) error {
|
||||||
|
req := types.UpdateDateTime{
|
||||||
|
This: s.Reference(),
|
||||||
|
DateTime: date,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateDateTime(ctx, s.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostDateTimeSystem) Query(ctx context.Context) (*time.Time, error) {
|
||||||
|
req := types.QueryDateTime{
|
||||||
|
This: s.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.QueryDateTime(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostFirewallSystem struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostFirewallSystem(c *vim25.Client, ref types.ManagedObjectReference) *HostFirewallSystem {
|
||||||
|
return &HostFirewallSystem{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostFirewallSystem) DisableRuleset(ctx context.Context, id string) error {
|
||||||
|
req := types.DisableRuleset{
|
||||||
|
This: s.Reference(),
|
||||||
|
Id: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.DisableRuleset(ctx, s.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostFirewallSystem) EnableRuleset(ctx context.Context, id string) error {
|
||||||
|
req := types.EnableRuleset{
|
||||||
|
This: s.Reference(),
|
||||||
|
Id: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.EnableRuleset(ctx, s.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostFirewallSystem) Refresh(ctx context.Context) error {
|
||||||
|
req := types.RefreshFirewall{
|
||||||
|
This: s.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RefreshFirewall(ctx, s.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostFirewallSystem) Info(ctx context.Context) (*types.HostFirewallInfo, error) {
|
||||||
|
var fs mo.HostFirewallSystem
|
||||||
|
|
||||||
|
err := s.Properties(ctx, s.Reference(), []string{"firewallInfo"}, &fs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.FirewallInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostFirewallRulesetList provides helpers for a slice of types.HostFirewallRuleset
|
||||||
|
type HostFirewallRulesetList []types.HostFirewallRuleset
|
||||||
|
|
||||||
|
// ByRule returns a HostFirewallRulesetList where Direction, PortType and Protocol are equal and Port is within range
|
||||||
|
func (l HostFirewallRulesetList) ByRule(rule types.HostFirewallRule) HostFirewallRulesetList {
|
||||||
|
var matches HostFirewallRulesetList
|
||||||
|
|
||||||
|
for _, rs := range l {
|
||||||
|
for _, r := range rs.Rule {
|
||||||
|
if r.PortType != rule.PortType ||
|
||||||
|
r.Protocol != rule.Protocol ||
|
||||||
|
r.Direction != rule.Direction {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.EndPort == 0 && rule.Port == r.Port ||
|
||||||
|
rule.Port >= r.Port && rule.Port <= r.EndPort {
|
||||||
|
matches = append(matches, rs)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnabledByRule returns a HostFirewallRulesetList with Match(rule) applied and filtered via Enabled()
|
||||||
|
// if enabled param is true, otherwise filtered via Disabled().
|
||||||
|
// An error is returned if the resulting list is empty.
|
||||||
|
func (l HostFirewallRulesetList) EnabledByRule(rule types.HostFirewallRule, enabled bool) (HostFirewallRulesetList, error) {
|
||||||
|
var matched, skipped HostFirewallRulesetList
|
||||||
|
var matchedKind, skippedKind string
|
||||||
|
|
||||||
|
l = l.ByRule(rule)
|
||||||
|
|
||||||
|
if enabled {
|
||||||
|
matched = l.Enabled()
|
||||||
|
matchedKind = "enabled"
|
||||||
|
|
||||||
|
skipped = l.Disabled()
|
||||||
|
skippedKind = "disabled"
|
||||||
|
} else {
|
||||||
|
matched = l.Disabled()
|
||||||
|
matchedKind = "disabled"
|
||||||
|
|
||||||
|
skipped = l.Enabled()
|
||||||
|
skippedKind = "enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(matched) == 0 {
|
||||||
|
msg := fmt.Sprintf("%d %s firewall rulesets match %s %s %s %d, %d %s rulesets match",
|
||||||
|
len(matched), matchedKind,
|
||||||
|
rule.Direction, rule.Protocol, rule.PortType, rule.Port,
|
||||||
|
len(skipped), skippedKind)
|
||||||
|
|
||||||
|
if len(skipped) != 0 {
|
||||||
|
msg += fmt.Sprintf(": %s", strings.Join(skipped.Keys(), ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return matched, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled returns a HostFirewallRulesetList with enabled rules
|
||||||
|
func (l HostFirewallRulesetList) Enabled() HostFirewallRulesetList {
|
||||||
|
var matches HostFirewallRulesetList
|
||||||
|
|
||||||
|
for _, rs := range l {
|
||||||
|
if rs.Enabled {
|
||||||
|
matches = append(matches, rs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disabled returns a HostFirewallRulesetList with disabled rules
|
||||||
|
func (l HostFirewallRulesetList) Disabled() HostFirewallRulesetList {
|
||||||
|
var matches HostFirewallRulesetList
|
||||||
|
|
||||||
|
for _, rs := range l {
|
||||||
|
if !rs.Enabled {
|
||||||
|
matches = append(matches, rs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns the HostFirewallRuleset.Key for each ruleset in the list
|
||||||
|
func (l HostFirewallRulesetList) Keys() []string {
|
||||||
|
var keys []string
|
||||||
|
|
||||||
|
for _, rs := range l {
|
||||||
|
keys = append(keys, rs.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
|
@ -0,0 +1,358 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostNetworkSystem struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostNetworkSystem(c *vim25.Client, ref types.ManagedObjectReference) *HostNetworkSystem {
|
||||||
|
return &HostNetworkSystem{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPortGroup wraps methods.AddPortGroup
|
||||||
|
func (o HostNetworkSystem) AddPortGroup(ctx context.Context, portgrp types.HostPortGroupSpec) error {
|
||||||
|
req := types.AddPortGroup{
|
||||||
|
This: o.Reference(),
|
||||||
|
Portgrp: portgrp,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.AddPortGroup(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddServiceConsoleVirtualNic wraps methods.AddServiceConsoleVirtualNic
|
||||||
|
func (o HostNetworkSystem) AddServiceConsoleVirtualNic(ctx context.Context, portgroup string, nic types.HostVirtualNicSpec) (string, error) {
|
||||||
|
req := types.AddServiceConsoleVirtualNic{
|
||||||
|
This: o.Reference(),
|
||||||
|
Portgroup: portgroup,
|
||||||
|
Nic: nic,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.AddServiceConsoleVirtualNic(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddVirtualNic wraps methods.AddVirtualNic
|
||||||
|
func (o HostNetworkSystem) AddVirtualNic(ctx context.Context, portgroup string, nic types.HostVirtualNicSpec) (string, error) {
|
||||||
|
req := types.AddVirtualNic{
|
||||||
|
This: o.Reference(),
|
||||||
|
Portgroup: portgroup,
|
||||||
|
Nic: nic,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.AddVirtualNic(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddVirtualSwitch wraps methods.AddVirtualSwitch
|
||||||
|
func (o HostNetworkSystem) AddVirtualSwitch(ctx context.Context, vswitchName string, spec *types.HostVirtualSwitchSpec) error {
|
||||||
|
req := types.AddVirtualSwitch{
|
||||||
|
This: o.Reference(),
|
||||||
|
VswitchName: vswitchName,
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.AddVirtualSwitch(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryNetworkHint wraps methods.QueryNetworkHint
|
||||||
|
func (o HostNetworkSystem) QueryNetworkHint(ctx context.Context, device []string) error {
|
||||||
|
req := types.QueryNetworkHint{
|
||||||
|
This: o.Reference(),
|
||||||
|
Device: device,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.QueryNetworkHint(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefreshNetworkSystem wraps methods.RefreshNetworkSystem
|
||||||
|
func (o HostNetworkSystem) RefreshNetworkSystem(ctx context.Context) error {
|
||||||
|
req := types.RefreshNetworkSystem{
|
||||||
|
This: o.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RefreshNetworkSystem(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemovePortGroup wraps methods.RemovePortGroup
|
||||||
|
func (o HostNetworkSystem) RemovePortGroup(ctx context.Context, pgName string) error {
|
||||||
|
req := types.RemovePortGroup{
|
||||||
|
This: o.Reference(),
|
||||||
|
PgName: pgName,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RemovePortGroup(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveServiceConsoleVirtualNic wraps methods.RemoveServiceConsoleVirtualNic
|
||||||
|
func (o HostNetworkSystem) RemoveServiceConsoleVirtualNic(ctx context.Context, device string) error {
|
||||||
|
req := types.RemoveServiceConsoleVirtualNic{
|
||||||
|
This: o.Reference(),
|
||||||
|
Device: device,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RemoveServiceConsoleVirtualNic(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveVirtualNic wraps methods.RemoveVirtualNic
|
||||||
|
func (o HostNetworkSystem) RemoveVirtualNic(ctx context.Context, device string) error {
|
||||||
|
req := types.RemoveVirtualNic{
|
||||||
|
This: o.Reference(),
|
||||||
|
Device: device,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RemoveVirtualNic(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveVirtualSwitch wraps methods.RemoveVirtualSwitch
|
||||||
|
func (o HostNetworkSystem) RemoveVirtualSwitch(ctx context.Context, vswitchName string) error {
|
||||||
|
req := types.RemoveVirtualSwitch{
|
||||||
|
This: o.Reference(),
|
||||||
|
VswitchName: vswitchName,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RemoveVirtualSwitch(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestartServiceConsoleVirtualNic wraps methods.RestartServiceConsoleVirtualNic
|
||||||
|
func (o HostNetworkSystem) RestartServiceConsoleVirtualNic(ctx context.Context, device string) error {
|
||||||
|
req := types.RestartServiceConsoleVirtualNic{
|
||||||
|
This: o.Reference(),
|
||||||
|
Device: device,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RestartServiceConsoleVirtualNic(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConsoleIpRouteConfig wraps methods.UpdateConsoleIpRouteConfig
|
||||||
|
func (o HostNetworkSystem) UpdateConsoleIpRouteConfig(ctx context.Context, config types.BaseHostIpRouteConfig) error {
|
||||||
|
req := types.UpdateConsoleIpRouteConfig{
|
||||||
|
This: o.Reference(),
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateConsoleIpRouteConfig(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateDnsConfig wraps methods.UpdateDnsConfig
|
||||||
|
func (o HostNetworkSystem) UpdateDnsConfig(ctx context.Context, config types.BaseHostDnsConfig) error {
|
||||||
|
req := types.UpdateDnsConfig{
|
||||||
|
This: o.Reference(),
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateDnsConfig(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateIpRouteConfig wraps methods.UpdateIpRouteConfig
|
||||||
|
func (o HostNetworkSystem) UpdateIpRouteConfig(ctx context.Context, config types.BaseHostIpRouteConfig) error {
|
||||||
|
req := types.UpdateIpRouteConfig{
|
||||||
|
This: o.Reference(),
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateIpRouteConfig(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateIpRouteTableConfig wraps methods.UpdateIpRouteTableConfig
|
||||||
|
func (o HostNetworkSystem) UpdateIpRouteTableConfig(ctx context.Context, config types.HostIpRouteTableConfig) error {
|
||||||
|
req := types.UpdateIpRouteTableConfig{
|
||||||
|
This: o.Reference(),
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateIpRouteTableConfig(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateNetworkConfig wraps methods.UpdateNetworkConfig
|
||||||
|
func (o HostNetworkSystem) UpdateNetworkConfig(ctx context.Context, config types.HostNetworkConfig, changeMode string) (*types.HostNetworkConfigResult, error) {
|
||||||
|
req := types.UpdateNetworkConfig{
|
||||||
|
This: o.Reference(),
|
||||||
|
Config: config,
|
||||||
|
ChangeMode: changeMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.UpdateNetworkConfig(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePhysicalNicLinkSpeed wraps methods.UpdatePhysicalNicLinkSpeed
|
||||||
|
func (o HostNetworkSystem) UpdatePhysicalNicLinkSpeed(ctx context.Context, device string, linkSpeed *types.PhysicalNicLinkInfo) error {
|
||||||
|
req := types.UpdatePhysicalNicLinkSpeed{
|
||||||
|
This: o.Reference(),
|
||||||
|
Device: device,
|
||||||
|
LinkSpeed: linkSpeed,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdatePhysicalNicLinkSpeed(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePortGroup wraps methods.UpdatePortGroup
|
||||||
|
func (o HostNetworkSystem) UpdatePortGroup(ctx context.Context, pgName string, portgrp types.HostPortGroupSpec) error {
|
||||||
|
req := types.UpdatePortGroup{
|
||||||
|
This: o.Reference(),
|
||||||
|
PgName: pgName,
|
||||||
|
Portgrp: portgrp,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdatePortGroup(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateServiceConsoleVirtualNic wraps methods.UpdateServiceConsoleVirtualNic
|
||||||
|
func (o HostNetworkSystem) UpdateServiceConsoleVirtualNic(ctx context.Context, device string, nic types.HostVirtualNicSpec) error {
|
||||||
|
req := types.UpdateServiceConsoleVirtualNic{
|
||||||
|
This: o.Reference(),
|
||||||
|
Device: device,
|
||||||
|
Nic: nic,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateServiceConsoleVirtualNic(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateVirtualNic wraps methods.UpdateVirtualNic
|
||||||
|
func (o HostNetworkSystem) UpdateVirtualNic(ctx context.Context, device string, nic types.HostVirtualNicSpec) error {
|
||||||
|
req := types.UpdateVirtualNic{
|
||||||
|
This: o.Reference(),
|
||||||
|
Device: device,
|
||||||
|
Nic: nic,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateVirtualNic(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateVirtualSwitch wraps methods.UpdateVirtualSwitch
|
||||||
|
func (o HostNetworkSystem) UpdateVirtualSwitch(ctx context.Context, vswitchName string, spec types.HostVirtualSwitchSpec) error {
|
||||||
|
req := types.UpdateVirtualSwitch{
|
||||||
|
This: o.Reference(),
|
||||||
|
VswitchName: vswitchName,
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateVirtualSwitch(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 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/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostServiceSystem struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostServiceSystem(c *vim25.Client, ref types.ManagedObjectReference) *HostServiceSystem {
|
||||||
|
return &HostServiceSystem{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostServiceSystem) Service(ctx context.Context) ([]types.HostService, error) {
|
||||||
|
var ss mo.HostServiceSystem
|
||||||
|
|
||||||
|
err := s.Properties(ctx, s.Reference(), []string{"serviceInfo.service"}, &ss)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.ServiceInfo.Service, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostServiceSystem) Start(ctx context.Context, id string) error {
|
||||||
|
req := types.StartService{
|
||||||
|
This: s.Reference(),
|
||||||
|
Id: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.StartService(ctx, s.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostServiceSystem) Stop(ctx context.Context, id string) error {
|
||||||
|
req := types.StopService{
|
||||||
|
This: s.Reference(),
|
||||||
|
Id: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.StopService(ctx, s.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostServiceSystem) Restart(ctx context.Context, id string) error {
|
||||||
|
req := types.RestartService{
|
||||||
|
This: s.Reference(),
|
||||||
|
Id: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RestartService(ctx, s.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostServiceSystem) UpdatePolicy(ctx context.Context, id string, policy string) error {
|
||||||
|
req := types.UpdateServicePolicy{
|
||||||
|
This: s.Reference(),
|
||||||
|
Id: id,
|
||||||
|
Policy: policy,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateServicePolicy(ctx, s.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostStorageSystem struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostStorageSystem(c *vim25.Client, ref types.ManagedObjectReference) *HostStorageSystem {
|
||||||
|
return &HostStorageSystem{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostStorageSystem) RetrieveDiskPartitionInfo(ctx context.Context, devicePath string) (*types.HostDiskPartitionInfo, error) {
|
||||||
|
req := types.RetrieveDiskPartitionInfo{
|
||||||
|
This: s.Reference(),
|
||||||
|
DevicePath: []string{devicePath},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.RetrieveDiskPartitionInfo(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Returnval == nil || len(res.Returnval) == 0 {
|
||||||
|
return nil, errors.New("no partition info")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostStorageSystem) ComputeDiskPartitionInfo(ctx context.Context, devicePath string, layout types.HostDiskPartitionLayout) (*types.HostDiskPartitionInfo, error) {
|
||||||
|
req := types.ComputeDiskPartitionInfo{
|
||||||
|
This: s.Reference(),
|
||||||
|
DevicePath: devicePath,
|
||||||
|
Layout: layout,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ComputeDiskPartitionInfo(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostStorageSystem) UpdateDiskPartitionInfo(ctx context.Context, devicePath string, spec types.HostDiskPartitionSpec) error {
|
||||||
|
req := types.UpdateDiskPartitions{
|
||||||
|
This: s.Reference(),
|
||||||
|
DevicePath: devicePath,
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateDiskPartitions(ctx, s.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostStorageSystem) RescanAllHba(ctx context.Context) error {
|
||||||
|
req := types.RescanAllHba{
|
||||||
|
This: s.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RescanAllHba(ctx, s.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostStorageSystem) MarkAsSsd(ctx context.Context, uuid string) (*Task, error) {
|
||||||
|
req := types.MarkAsSsd_Task{
|
||||||
|
This: s.Reference(),
|
||||||
|
ScsiDiskUuid: uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.MarkAsSsd_Task(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(s.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostStorageSystem) MarkAsNonSsd(ctx context.Context, uuid string) (*Task, error) {
|
||||||
|
req := types.MarkAsNonSsd_Task{
|
||||||
|
This: s.Reference(),
|
||||||
|
ScsiDiskUuid: uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.MarkAsNonSsd_Task(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(s.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostStorageSystem) MarkAsLocal(ctx context.Context, uuid string) (*Task, error) {
|
||||||
|
req := types.MarkAsLocal_Task{
|
||||||
|
This: s.Reference(),
|
||||||
|
ScsiDiskUuid: uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.MarkAsLocal_Task(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(s.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostStorageSystem) MarkAsNonLocal(ctx context.Context, uuid string) (*Task, error) {
|
||||||
|
req := types.MarkAsNonLocal_Task{
|
||||||
|
This: s.Reference(),
|
||||||
|
ScsiDiskUuid: uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.MarkAsNonLocal_Task(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(s.c, res.Returnval), nil
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostSystem struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostSystem(c *vim25.Client, ref types.ManagedObjectReference) *HostSystem {
|
||||||
|
return &HostSystem{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HostSystem) ConfigManager() *HostConfigManager {
|
||||||
|
return NewHostConfigManager(h.c, h.Reference())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HostSystem) ResourcePool(ctx context.Context) (*ResourcePool, error) {
|
||||||
|
var mh mo.HostSystem
|
||||||
|
|
||||||
|
err := h.Properties(ctx, h.Reference(), []string{"parent"}, &mh)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var mcr *mo.ComputeResource
|
||||||
|
var parent interface{}
|
||||||
|
|
||||||
|
switch mh.Parent.Type {
|
||||||
|
case "ComputeResource":
|
||||||
|
mcr = new(mo.ComputeResource)
|
||||||
|
parent = mcr
|
||||||
|
case "ClusterComputeResource":
|
||||||
|
mcc := new(mo.ClusterComputeResource)
|
||||||
|
mcr = &mcc.ComputeResource
|
||||||
|
parent = mcc
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown host parent type: %s", mh.Parent.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.Properties(ctx, *mh.Parent, []string{"resourcePool"}, parent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pool := NewResourcePool(h.c, *mcr.ResourcePool)
|
||||||
|
return pool, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HostSystem) ManagementIPs(ctx context.Context) ([]net.IP, error) {
|
||||||
|
var mh mo.HostSystem
|
||||||
|
|
||||||
|
err := h.Properties(ctx, h.Reference(), []string{"config.virtualNicManagerInfo.netConfig"}, &mh)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ips []net.IP
|
||||||
|
for _, nc := range mh.Config.VirtualNicManagerInfo.NetConfig {
|
||||||
|
if nc.NicType == "management" && len(nc.CandidateVnic) > 0 {
|
||||||
|
ip := net.ParseIP(nc.CandidateVnic[0].Spec.Ip.IpAddress)
|
||||||
|
if ip != nil {
|
||||||
|
ips = append(ips, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HostSystem) Disconnect(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.DisconnectHost_Task{
|
||||||
|
This: h.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.DisconnectHost_Task(ctx, h.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(h.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HostSystem) Reconnect(ctx context.Context, cnxSpec *types.HostConnectSpec, reconnectSpec *types.HostSystemReconnectSpec) (*Task, error) {
|
||||||
|
req := types.ReconnectHost_Task{
|
||||||
|
This: h.Reference(),
|
||||||
|
CnxSpec: cnxSpec,
|
||||||
|
ReconnectSpec: reconnectSpec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ReconnectHost_Task(ctx, h.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(h.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HostSystem) EnterMaintenanceMode(ctx context.Context, timeout int32, evacuate bool, spec *types.HostMaintenanceSpec) (*Task, error) {
|
||||||
|
req := types.EnterMaintenanceMode_Task{
|
||||||
|
This: h.Reference(),
|
||||||
|
Timeout: timeout,
|
||||||
|
EvacuatePoweredOffVms: types.NewBool(evacuate),
|
||||||
|
MaintenanceSpec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.EnterMaintenanceMode_Task(ctx, h.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(h.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HostSystem) ExitMaintenanceMode(ctx context.Context, timeout int32) (*Task, error) {
|
||||||
|
req := types.ExitMaintenanceMode_Task{
|
||||||
|
This: h.Reference(),
|
||||||
|
Timeout: timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ExitMaintenanceMode_Task(ctx, h.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(h.c, res.Returnval), nil
|
||||||
|
}
|
93
vendor/github.com/vmware/govmomi/object/host_virtual_nic_manager.go
generated
vendored
Normal file
93
vendor/github.com/vmware/govmomi/object/host_virtual_nic_manager.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostVirtualNicManager struct {
|
||||||
|
Common
|
||||||
|
Host *HostSystem
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostVirtualNicManager(c *vim25.Client, ref types.ManagedObjectReference, host types.ManagedObjectReference) *HostVirtualNicManager {
|
||||||
|
return &HostVirtualNicManager{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
Host: NewHostSystem(c, host),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostVirtualNicManager) Info(ctx context.Context) (*types.HostVirtualNicManagerInfo, error) {
|
||||||
|
var vnm mo.HostVirtualNicManager
|
||||||
|
|
||||||
|
err := m.Properties(ctx, m.Reference(), []string{"info"}, &vnm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &vnm.Info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostVirtualNicManager) DeselectVnic(ctx context.Context, nicType string, device string) error {
|
||||||
|
if nicType == string(types.HostVirtualNicManagerNicTypeVsan) {
|
||||||
|
// Avoid fault.NotSupported:
|
||||||
|
// "Error deselecting device '$device': VSAN interfaces must be deselected using vim.host.VsanSystem"
|
||||||
|
s, err := m.Host.ConfigManager().VsanSystem(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.updateVnic(ctx, device, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.DeselectVnicForNicType{
|
||||||
|
This: m.Reference(),
|
||||||
|
NicType: nicType,
|
||||||
|
Device: device,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.DeselectVnicForNicType(ctx, m.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostVirtualNicManager) SelectVnic(ctx context.Context, nicType string, device string) error {
|
||||||
|
if nicType == string(types.HostVirtualNicManagerNicTypeVsan) {
|
||||||
|
// Avoid fault.NotSupported:
|
||||||
|
// "Error selecting device '$device': VSAN interfaces must be selected using vim.host.VsanSystem"
|
||||||
|
s, err := m.Host.ConfigManager().VsanSystem(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.updateVnic(ctx, device, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.SelectVnicForNicType{
|
||||||
|
This: m.Reference(),
|
||||||
|
NicType: nicType,
|
||||||
|
Device: device,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.SelectVnicForNicType(ctx, m.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
117
vendor/github.com/vmware/govmomi/object/host_vsan_internal_system.go
generated
vendored
Normal file
117
vendor/github.com/vmware/govmomi/object/host_vsan_internal_system.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 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"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostVsanInternalSystem struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostVsanInternalSystem(c *vim25.Client, ref types.ManagedObjectReference) *HostVsanInternalSystem {
|
||||||
|
m := HostVsanInternalSystem{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVsanObjectUuidsByFilter returns vSAN DOM object uuids by filter.
|
||||||
|
func (m HostVsanInternalSystem) QueryVsanObjectUuidsByFilter(ctx context.Context, uuids []string, limit int32, version int32) ([]string, error) {
|
||||||
|
req := types.QueryVsanObjectUuidsByFilter{
|
||||||
|
This: m.Reference(),
|
||||||
|
Uuids: uuids,
|
||||||
|
Limit: limit,
|
||||||
|
Version: version,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.QueryVsanObjectUuidsByFilter(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type VsanObjExtAttrs struct {
|
||||||
|
Type string `json:"Object type"`
|
||||||
|
Class string `json:"Object class"`
|
||||||
|
Size string `json:"Object size"`
|
||||||
|
Path string `json:"Object path"`
|
||||||
|
Name string `json:"User friendly name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *VsanObjExtAttrs) DatastorePath(dir string) string {
|
||||||
|
l := len(dir)
|
||||||
|
path := a.Path
|
||||||
|
|
||||||
|
if len(path) >= l {
|
||||||
|
path = a.Path[l:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if path != "" {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.Name // vmnamespace
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVsanObjExtAttrs is internal and intended for troubleshooting/debugging situations in the field.
|
||||||
|
// WARNING: This API can be slow because we do IOs (reads) to all the objects.
|
||||||
|
func (m HostVsanInternalSystem) GetVsanObjExtAttrs(ctx context.Context, uuids []string) (map[string]VsanObjExtAttrs, error) {
|
||||||
|
req := types.GetVsanObjExtAttrs{
|
||||||
|
This: m.Reference(),
|
||||||
|
Uuids: uuids,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.GetVsanObjExtAttrs(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var attrs map[string]VsanObjExtAttrs
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(res.Returnval), &attrs)
|
||||||
|
|
||||||
|
return attrs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteVsanObjects is internal and intended for troubleshooting/debugging only.
|
||||||
|
// WARNING: This API can be slow because we do IOs to all the objects.
|
||||||
|
// DOM won't allow access to objects which have lost quorum. Such objects can be deleted with the optional "force" flag.
|
||||||
|
// These objects may however re-appear with quorum if the absent components come back (network partition gets resolved, etc.)
|
||||||
|
func (m HostVsanInternalSystem) DeleteVsanObjects(ctx context.Context, uuids []string, force *bool) ([]types.HostVsanInternalSystemDeleteVsanObjectsResult, error) {
|
||||||
|
req := types.DeleteVsanObjects{
|
||||||
|
This: m.Reference(),
|
||||||
|
Uuids: uuids,
|
||||||
|
Force: force,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.DeleteVsanObjects(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HostVsanSystem struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostVsanSystem(c *vim25.Client, ref types.ManagedObjectReference) *HostVsanSystem {
|
||||||
|
return &HostVsanSystem{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HostVsanSystem) Update(ctx context.Context, config types.VsanHostConfigInfo) (*Task, error) {
|
||||||
|
req := types.UpdateVsan_Task{
|
||||||
|
This: s.Reference(),
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.UpdateVsan_Task(ctx, s.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(s.Client(), res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateVnic in support of the HostVirtualNicManager.{SelectVnic,DeselectVnic} methods
|
||||||
|
func (s HostVsanSystem) updateVnic(ctx context.Context, device string, enable bool) error {
|
||||||
|
var vsan mo.HostVsanSystem
|
||||||
|
|
||||||
|
err := s.Properties(ctx, s.Reference(), []string{"config.networkInfo.port"}, &vsan)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := vsan.Config
|
||||||
|
|
||||||
|
var port []types.VsanHostConfigInfoNetworkInfoPortConfig
|
||||||
|
|
||||||
|
for _, p := range info.NetworkInfo.Port {
|
||||||
|
if p.Device == device {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
port = append(port, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enable {
|
||||||
|
port = append(port, types.VsanHostConfigInfoNetworkInfoPortConfig{
|
||||||
|
Device: device,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
info.NetworkInfo.Port = port
|
||||||
|
|
||||||
|
task, err := s.Update(ctx, info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = task.WaitForResult(ctx, nil)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/property"
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HttpNfcLease struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHttpNfcLease(c *vim25.Client, ref types.ManagedObjectReference) *HttpNfcLease {
|
||||||
|
return &HttpNfcLease{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpNfcLeaseAbort wraps methods.HttpNfcLeaseAbort
|
||||||
|
func (o HttpNfcLease) HttpNfcLeaseAbort(ctx context.Context, fault *types.LocalizedMethodFault) error {
|
||||||
|
req := types.HttpNfcLeaseAbort{
|
||||||
|
This: o.Reference(),
|
||||||
|
Fault: fault,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.HttpNfcLeaseAbort(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpNfcLeaseComplete wraps methods.HttpNfcLeaseComplete
|
||||||
|
func (o HttpNfcLease) HttpNfcLeaseComplete(ctx context.Context) error {
|
||||||
|
req := types.HttpNfcLeaseComplete{
|
||||||
|
This: o.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.HttpNfcLeaseComplete(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpNfcLeaseGetManifest wraps methods.HttpNfcLeaseGetManifest
|
||||||
|
func (o HttpNfcLease) HttpNfcLeaseGetManifest(ctx context.Context) error {
|
||||||
|
req := types.HttpNfcLeaseGetManifest{
|
||||||
|
This: o.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.HttpNfcLeaseGetManifest(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpNfcLeaseProgress wraps methods.HttpNfcLeaseProgress
|
||||||
|
func (o HttpNfcLease) HttpNfcLeaseProgress(ctx context.Context, percent int32) error {
|
||||||
|
req := types.HttpNfcLeaseProgress{
|
||||||
|
This: o.Reference(),
|
||||||
|
Percent: percent,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.HttpNfcLeaseProgress(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o HttpNfcLease) Wait(ctx context.Context) (*types.HttpNfcLeaseInfo, error) {
|
||||||
|
var lease mo.HttpNfcLease
|
||||||
|
|
||||||
|
pc := property.DefaultCollector(o.c)
|
||||||
|
err := property.Wait(ctx, pc, o.Reference(), []string{"state", "info", "error"}, func(pc []types.PropertyChange) bool {
|
||||||
|
done := false
|
||||||
|
|
||||||
|
for _, c := range pc {
|
||||||
|
if c.Val == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c.Name {
|
||||||
|
case "error":
|
||||||
|
val := c.Val.(types.LocalizedMethodFault)
|
||||||
|
lease.Error = &val
|
||||||
|
done = true
|
||||||
|
case "info":
|
||||||
|
val := c.Val.(types.HttpNfcLeaseInfo)
|
||||||
|
lease.Info = &val
|
||||||
|
case "state":
|
||||||
|
lease.State = c.Val.(types.HttpNfcLeaseState)
|
||||||
|
if lease.State != types.HttpNfcLeaseStateInitializing {
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return done
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if lease.State == types.HttpNfcLeaseStateReady {
|
||||||
|
return lease.Info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if lease.Error != nil {
|
||||||
|
return nil, errors.New(lease.Error.LocalizedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("unexpected nfc lease state: %s", lease.State)
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DatastoreNamespaceManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDatastoreNamespaceManager(c *vim25.Client) *DatastoreNamespaceManager {
|
||||||
|
n := DatastoreNamespaceManager{
|
||||||
|
Common: NewCommon(c, *c.ServiceContent.DatastoreNamespaceManager),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &n
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDirectory creates a top-level directory on the given vsan datastore, using
|
||||||
|
// the given user display name hint and opaque storage policy.
|
||||||
|
func (nm DatastoreNamespaceManager) CreateDirectory(ctx context.Context, ds *Datastore, displayName string, policy string) (string, error) {
|
||||||
|
|
||||||
|
req := &types.CreateDirectory{
|
||||||
|
This: nm.Reference(),
|
||||||
|
Datastore: ds.Reference(),
|
||||||
|
DisplayName: displayName,
|
||||||
|
Policy: policy,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := methods.CreateDirectory(ctx, nm.c, req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteDirectory deletes the given top-level directory from a vsan datastore.
|
||||||
|
func (nm DatastoreNamespaceManager) DeleteDirectory(ctx context.Context, dc *Datacenter, datastorePath string) error {
|
||||||
|
|
||||||
|
req := &types.DeleteDirectory{
|
||||||
|
This: nm.Reference(),
|
||||||
|
DatastorePath: datastorePath,
|
||||||
|
}
|
||||||
|
|
||||||
|
if dc != nil {
|
||||||
|
ref := dc.Reference()
|
||||||
|
req.Datacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := methods.DeleteDirectory(ctx, nm.c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Network struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNetwork(c *vim25.Client, ref types.ManagedObjectReference) *Network {
|
||||||
|
return &Network{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EthernetCardBackingInfo returns the VirtualDeviceBackingInfo for this Network
|
||||||
|
func (n Network) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
|
||||||
|
var e mo.Network
|
||||||
|
|
||||||
|
// Use Network.Name rather than Common.Name as the latter does not return the complete name if it contains a '/'
|
||||||
|
// We can't use Common.ObjectName here either as we need the ManagedEntity.Name field is not set since mo.Network
|
||||||
|
// has its own Name field.
|
||||||
|
err := n.Properties(ctx, n.Reference(), []string{"name"}, &e)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
backing := &types.VirtualEthernetCardNetworkBackingInfo{
|
||||||
|
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
|
||||||
|
DeviceName: e.Name,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return backing, nil
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The NetworkReference interface is implemented by managed objects
|
||||||
|
// which can be used as the backing for a VirtualEthernetCard.
|
||||||
|
type NetworkReference interface {
|
||||||
|
Reference
|
||||||
|
|
||||||
|
EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error)
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 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"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OpaqueNetwork struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOpaqueNetwork(c *vim25.Client, ref types.ManagedObjectReference) *OpaqueNetwork {
|
||||||
|
return &OpaqueNetwork{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EthernetCardBackingInfo returns the VirtualDeviceBackingInfo for this Network
|
||||||
|
func (n OpaqueNetwork) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
|
||||||
|
var net mo.OpaqueNetwork
|
||||||
|
|
||||||
|
if err := n.Properties(ctx, n.Reference(), []string{"summary"}, &net); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
summary, ok := net.Summary.(*types.OpaqueNetworkSummary)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%s unsupported network type: %T", n, net.Summary)
|
||||||
|
}
|
||||||
|
|
||||||
|
backing := &types.VirtualEthernetCardOpaqueNetworkBackingInfo{
|
||||||
|
OpaqueNetworkId: summary.OpaqueNetworkId,
|
||||||
|
OpaqueNetworkType: summary.OpaqueNetworkType,
|
||||||
|
}
|
||||||
|
|
||||||
|
return backing, nil
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OptionManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOptionManager(c *vim25.Client, ref types.ManagedObjectReference) *OptionManager {
|
||||||
|
return &OptionManager{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m OptionManager) Query(ctx context.Context, name string) ([]types.BaseOptionValue, error) {
|
||||||
|
req := types.QueryOptions{
|
||||||
|
This: m.Reference(),
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.QueryOptions(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m OptionManager) Update(ctx context.Context, value []types.BaseOptionValue) error {
|
||||||
|
req := types.UpdateOptions{
|
||||||
|
This: m.Reference(),
|
||||||
|
ChangedValue: value,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateOptions(ctx, m.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OvfManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOvfManager(c *vim25.Client) *OvfManager {
|
||||||
|
o := OvfManager{
|
||||||
|
Common: NewCommon(c, *c.ServiceContent.OvfManager),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &o
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDescriptor wraps methods.CreateDescriptor
|
||||||
|
func (o OvfManager) CreateDescriptor(ctx context.Context, obj Reference, cdp types.OvfCreateDescriptorParams) (*types.OvfCreateDescriptorResult, error) {
|
||||||
|
req := types.CreateDescriptor{
|
||||||
|
This: o.Reference(),
|
||||||
|
Obj: obj.Reference(),
|
||||||
|
Cdp: cdp,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateDescriptor(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateImportSpec wraps methods.CreateImportSpec
|
||||||
|
func (o OvfManager) CreateImportSpec(ctx context.Context, ovfDescriptor string, resourcePool Reference, datastore Reference, cisp types.OvfCreateImportSpecParams) (*types.OvfCreateImportSpecResult, error) {
|
||||||
|
req := types.CreateImportSpec{
|
||||||
|
This: o.Reference(),
|
||||||
|
OvfDescriptor: ovfDescriptor,
|
||||||
|
ResourcePool: resourcePool.Reference(),
|
||||||
|
Datastore: datastore.Reference(),
|
||||||
|
Cisp: cisp,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateImportSpec(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseDescriptor wraps methods.ParseDescriptor
|
||||||
|
func (o OvfManager) ParseDescriptor(ctx context.Context, ovfDescriptor string, pdp types.OvfParseDescriptorParams) (*types.OvfParseDescriptorResult, error) {
|
||||||
|
req := types.ParseDescriptor{
|
||||||
|
This: o.Reference(),
|
||||||
|
OvfDescriptor: ovfDescriptor,
|
||||||
|
Pdp: pdp,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ParseDescriptor(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateHost wraps methods.ValidateHost
|
||||||
|
func (o OvfManager) ValidateHost(ctx context.Context, ovfDescriptor string, host Reference, vhp types.OvfValidateHostParams) (*types.OvfValidateHostResult, error) {
|
||||||
|
req := types.ValidateHost{
|
||||||
|
This: o.Reference(),
|
||||||
|
OvfDescriptor: ovfDescriptor,
|
||||||
|
Host: host.Reference(),
|
||||||
|
Vhp: vhp,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ValidateHost(ctx, o.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResourcePool struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResourcePool(c *vim25.Client, ref types.ManagedObjectReference) *ResourcePool {
|
||||||
|
return &ResourcePool{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ResourcePool) ImportVApp(ctx context.Context, spec types.BaseImportSpec, folder *Folder, host *HostSystem) (*HttpNfcLease, error) {
|
||||||
|
req := types.ImportVApp{
|
||||||
|
This: p.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
if folder != nil {
|
||||||
|
ref := folder.Reference()
|
||||||
|
req.Folder = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
if host != nil {
|
||||||
|
ref := host.Reference()
|
||||||
|
req.Host = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ImportVApp(ctx, p.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHttpNfcLease(p.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ResourcePool) Create(ctx context.Context, name string, spec types.ResourceConfigSpec) (*ResourcePool, error) {
|
||||||
|
req := types.CreateResourcePool{
|
||||||
|
This: p.Reference(),
|
||||||
|
Name: name,
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateResourcePool(ctx, p.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewResourcePool(p.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ResourcePool) CreateVApp(ctx context.Context, name string, resSpec types.ResourceConfigSpec, configSpec types.VAppConfigSpec, folder *Folder) (*VirtualApp, error) {
|
||||||
|
req := types.CreateVApp{
|
||||||
|
This: p.Reference(),
|
||||||
|
Name: name,
|
||||||
|
ResSpec: resSpec,
|
||||||
|
ConfigSpec: configSpec,
|
||||||
|
}
|
||||||
|
|
||||||
|
if folder != nil {
|
||||||
|
ref := folder.Reference()
|
||||||
|
req.VmFolder = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateVApp(ctx, p.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewVirtualApp(p.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ResourcePool) UpdateConfig(ctx context.Context, name string, config *types.ResourceConfigSpec) error {
|
||||||
|
req := types.UpdateConfig{
|
||||||
|
This: p.Reference(),
|
||||||
|
Name: name,
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
if config != nil && config.Entity == nil {
|
||||||
|
ref := p.Reference()
|
||||||
|
|
||||||
|
// Create copy of config so changes won't leak back to the caller
|
||||||
|
newConfig := *config
|
||||||
|
newConfig.Entity = &ref
|
||||||
|
req.Config = &newConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateConfig(ctx, p.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ResourcePool) DestroyChildren(ctx context.Context) error {
|
||||||
|
req := types.DestroyChildren{
|
||||||
|
This: p.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.DestroyChildren(ctx, p.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ResourcePool) Destroy(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.Destroy_Task{
|
||||||
|
This: p.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.Destroy_Task(ctx, p.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(p.c, res.Returnval), nil
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SearchIndex struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSearchIndex(c *vim25.Client) *SearchIndex {
|
||||||
|
s := SearchIndex{
|
||||||
|
Common: NewCommon(c, *c.ServiceContent.SearchIndex),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindByDatastorePath finds a virtual machine by its location on a datastore.
|
||||||
|
func (s SearchIndex) FindByDatastorePath(ctx context.Context, dc *Datacenter, path string) (Reference, error) {
|
||||||
|
req := types.FindByDatastorePath{
|
||||||
|
This: s.Reference(),
|
||||||
|
Datacenter: dc.Reference(),
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.FindByDatastorePath(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Returnval == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return NewReference(s.c, *res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindByDnsName finds a virtual machine or host by DNS name.
|
||||||
|
func (s SearchIndex) FindByDnsName(ctx context.Context, dc *Datacenter, dnsName string, vmSearch bool) (Reference, error) {
|
||||||
|
req := types.FindByDnsName{
|
||||||
|
This: s.Reference(),
|
||||||
|
DnsName: dnsName,
|
||||||
|
VmSearch: vmSearch,
|
||||||
|
}
|
||||||
|
if dc != nil {
|
||||||
|
ref := dc.Reference()
|
||||||
|
req.Datacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.FindByDnsName(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Returnval == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return NewReference(s.c, *res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindByInventoryPath finds a managed entity based on its location in the inventory.
|
||||||
|
func (s SearchIndex) FindByInventoryPath(ctx context.Context, path string) (Reference, error) {
|
||||||
|
req := types.FindByInventoryPath{
|
||||||
|
This: s.Reference(),
|
||||||
|
InventoryPath: path,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.FindByInventoryPath(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Returnval == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return NewReference(s.c, *res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindByIp finds a virtual machine or host by IP address.
|
||||||
|
func (s SearchIndex) FindByIp(ctx context.Context, dc *Datacenter, ip string, vmSearch bool) (Reference, error) {
|
||||||
|
req := types.FindByIp{
|
||||||
|
This: s.Reference(),
|
||||||
|
Ip: ip,
|
||||||
|
VmSearch: vmSearch,
|
||||||
|
}
|
||||||
|
if dc != nil {
|
||||||
|
ref := dc.Reference()
|
||||||
|
req.Datacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.FindByIp(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Returnval == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return NewReference(s.c, *res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindByUuid finds a virtual machine or host by UUID.
|
||||||
|
func (s SearchIndex) FindByUuid(ctx context.Context, dc *Datacenter, uuid string, vmSearch bool, instanceUuid *bool) (Reference, error) {
|
||||||
|
req := types.FindByUuid{
|
||||||
|
This: s.Reference(),
|
||||||
|
Uuid: uuid,
|
||||||
|
VmSearch: vmSearch,
|
||||||
|
InstanceUuid: instanceUuid,
|
||||||
|
}
|
||||||
|
if dc != nil {
|
||||||
|
ref := dc.Reference()
|
||||||
|
req.Datacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.FindByUuid(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Returnval == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return NewReference(s.c, *res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindChild finds a particular child based on a managed entity name.
|
||||||
|
func (s SearchIndex) FindChild(ctx context.Context, entity Reference, name string) (Reference, error) {
|
||||||
|
req := types.FindChild{
|
||||||
|
This: s.Reference(),
|
||||||
|
Entity: entity.Reference(),
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.FindChild(ctx, s.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Returnval == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return NewReference(s.c, *res.Returnval), nil
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 (
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StoragePod struct {
|
||||||
|
*Folder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStoragePod(c *vim25.Client, ref types.ManagedObjectReference) *StoragePod {
|
||||||
|
return &StoragePod{
|
||||||
|
Folder: &Folder{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
179
vendor/github.com/vmware/govmomi/object/storage_resource_manager.go
generated
vendored
Normal file
179
vendor/github.com/vmware/govmomi/object/storage_resource_manager.go
generated
vendored
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StorageResourceManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStorageResourceManager(c *vim25.Client) *StorageResourceManager {
|
||||||
|
sr := StorageResourceManager{
|
||||||
|
Common: NewCommon(c, *c.ServiceContent.StorageResourceManager),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr StorageResourceManager) ApplyStorageDrsRecommendation(ctx context.Context, key []string) (*Task, error) {
|
||||||
|
req := types.ApplyStorageDrsRecommendation_Task{
|
||||||
|
This: sr.Reference(),
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ApplyStorageDrsRecommendation_Task(ctx, sr.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(sr.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr StorageResourceManager) ApplyStorageDrsRecommendationToPod(ctx context.Context, pod *StoragePod, key string) (*Task, error) {
|
||||||
|
req := types.ApplyStorageDrsRecommendationToPod_Task{
|
||||||
|
This: sr.Reference(),
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
if pod != nil {
|
||||||
|
req.Pod = pod.Reference()
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ApplyStorageDrsRecommendationToPod_Task(ctx, sr.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(sr.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr StorageResourceManager) CancelStorageDrsRecommendation(ctx context.Context, key []string) error {
|
||||||
|
req := types.CancelStorageDrsRecommendation{
|
||||||
|
This: sr.Reference(),
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.CancelStorageDrsRecommendation(ctx, sr.c, &req)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr StorageResourceManager) ConfigureDatastoreIORM(ctx context.Context, datastore *Datastore, spec types.StorageIORMConfigSpec, key string) (*Task, error) {
|
||||||
|
req := types.ConfigureDatastoreIORM_Task{
|
||||||
|
This: sr.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
if datastore != nil {
|
||||||
|
req.Datastore = datastore.Reference()
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ConfigureDatastoreIORM_Task(ctx, sr.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(sr.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr StorageResourceManager) ConfigureStorageDrsForPod(ctx context.Context, pod *StoragePod, spec types.StorageDrsConfigSpec, modify bool) (*Task, error) {
|
||||||
|
req := types.ConfigureStorageDrsForPod_Task{
|
||||||
|
This: sr.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
Modify: modify,
|
||||||
|
}
|
||||||
|
|
||||||
|
if pod != nil {
|
||||||
|
req.Pod = pod.Reference()
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ConfigureStorageDrsForPod_Task(ctx, sr.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(sr.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr StorageResourceManager) QueryDatastorePerformanceSummary(ctx context.Context, datastore *Datastore) ([]types.StoragePerformanceSummary, error) {
|
||||||
|
req := types.QueryDatastorePerformanceSummary{
|
||||||
|
This: sr.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if datastore != nil {
|
||||||
|
req.Datastore = datastore.Reference()
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.QueryDatastorePerformanceSummary(ctx, sr.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr StorageResourceManager) QueryIORMConfigOption(ctx context.Context, host *HostSystem) (*types.StorageIORMConfigOption, error) {
|
||||||
|
req := types.QueryIORMConfigOption{
|
||||||
|
This: sr.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if host != nil {
|
||||||
|
req.Host = host.Reference()
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.QueryIORMConfigOption(ctx, sr.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr StorageResourceManager) RecommendDatastores(ctx context.Context, storageSpec types.StoragePlacementSpec) (*types.StoragePlacementResult, error) {
|
||||||
|
req := types.RecommendDatastores{
|
||||||
|
This: sr.Reference(),
|
||||||
|
StorageSpec: storageSpec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.RecommendDatastores(ctx, sr.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr StorageResourceManager) RefreshStorageDrsRecommendation(ctx context.Context, pod *StoragePod) error {
|
||||||
|
req := types.RefreshStorageDrsRecommendation{
|
||||||
|
This: sr.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if pod != nil {
|
||||||
|
req.Pod = pod.Reference()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RefreshStorageDrsRecommendation(ctx, sr.c, &req)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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/property"
|
||||||
|
"github.com/vmware/govmomi/task"
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/progress"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Task is a convenience wrapper around task.Task that keeps a reference to
|
||||||
|
// the client that was used to create it. This allows users to call the Wait()
|
||||||
|
// function with only a context parameter, instead of a context parameter, a
|
||||||
|
// soap.RoundTripper, and reference to the root property collector.
|
||||||
|
type Task struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTask(c *vim25.Client, ref types.ManagedObjectReference) *Task {
|
||||||
|
t := Task{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) Wait(ctx context.Context) error {
|
||||||
|
_, err := t.WaitForResult(ctx, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) WaitForResult(ctx context.Context, s progress.Sinker) (*types.TaskInfo, error) {
|
||||||
|
p := property.DefaultCollector(t.c)
|
||||||
|
return task.Wait(ctx, t.Reference(), p, s)
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 (
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Reference interface {
|
||||||
|
Reference() types.ManagedObjectReference
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReference(c *vim25.Client, e types.ManagedObjectReference) Reference {
|
||||||
|
switch e.Type {
|
||||||
|
case "Folder":
|
||||||
|
return NewFolder(c, e)
|
||||||
|
case "StoragePod":
|
||||||
|
return &StoragePod{
|
||||||
|
NewFolder(c, e),
|
||||||
|
}
|
||||||
|
case "Datacenter":
|
||||||
|
return NewDatacenter(c, e)
|
||||||
|
case "VirtualMachine":
|
||||||
|
return NewVirtualMachine(c, e)
|
||||||
|
case "VirtualApp":
|
||||||
|
return &VirtualApp{
|
||||||
|
NewResourcePool(c, e),
|
||||||
|
}
|
||||||
|
case "ComputeResource":
|
||||||
|
return NewComputeResource(c, e)
|
||||||
|
case "ClusterComputeResource":
|
||||||
|
return NewClusterComputeResource(c, e)
|
||||||
|
case "HostSystem":
|
||||||
|
return NewHostSystem(c, e)
|
||||||
|
case "Network", "OpaqueNetwork":
|
||||||
|
return NewNetwork(c, e)
|
||||||
|
case "ResourcePool":
|
||||||
|
return NewResourcePool(c, e)
|
||||||
|
case "DistributedVirtualSwitch":
|
||||||
|
return NewDistributedVirtualSwitch(c, e)
|
||||||
|
case "VmwareDistributedVirtualSwitch":
|
||||||
|
return &VmwareDistributedVirtualSwitch{*NewDistributedVirtualSwitch(c, e)}
|
||||||
|
case "DistributedVirtualPortgroup":
|
||||||
|
return NewDistributedVirtualPortgroup(c, e)
|
||||||
|
case "Datastore":
|
||||||
|
return NewDatastore(c, e)
|
||||||
|
default:
|
||||||
|
panic("Unknown managed entity: " + e.Type)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VirtualApp struct {
|
||||||
|
*ResourcePool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVirtualApp(c *vim25.Client, ref types.ManagedObjectReference) *VirtualApp {
|
||||||
|
return &VirtualApp{
|
||||||
|
ResourcePool: NewResourcePool(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p VirtualApp) CreateChildVM(ctx context.Context, config types.VirtualMachineConfigSpec, host *HostSystem) (*Task, error) {
|
||||||
|
req := types.CreateChildVM_Task{
|
||||||
|
This: p.Reference(),
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
if host != nil {
|
||||||
|
ref := host.Reference()
|
||||||
|
req.Host = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateChildVM_Task(ctx, p.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(p.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p VirtualApp) UpdateConfig(ctx context.Context, spec types.VAppConfigSpec) error {
|
||||||
|
req := types.UpdateVAppConfig{
|
||||||
|
This: p.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UpdateVAppConfig(ctx, p.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p VirtualApp) PowerOn(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.PowerOnVApp_Task{
|
||||||
|
This: p.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.PowerOnVApp_Task(ctx, p.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(p.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p VirtualApp) PowerOff(ctx context.Context, force bool) (*Task, error) {
|
||||||
|
req := types.PowerOffVApp_Task{
|
||||||
|
This: p.Reference(),
|
||||||
|
Force: force,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.PowerOffVApp_Task(ctx, p.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(p.c, res.Returnval), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p VirtualApp) Suspend(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.SuspendVApp_Task{
|
||||||
|
This: p.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.SuspendVApp_Task(ctx, p.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(p.c, res.Returnval), nil
|
||||||
|
}
|
|
@ -0,0 +1,922 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type values for use in BootOrder
|
||||||
|
const (
|
||||||
|
DeviceTypeCdrom = "cdrom"
|
||||||
|
DeviceTypeDisk = "disk"
|
||||||
|
DeviceTypeEthernet = "ethernet"
|
||||||
|
DeviceTypeFloppy = "floppy"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VirtualDeviceList provides helper methods for working with a list of virtual devices.
|
||||||
|
type VirtualDeviceList []types.BaseVirtualDevice
|
||||||
|
|
||||||
|
// SCSIControllerTypes are used for adding a new SCSI controller to a VM.
|
||||||
|
func SCSIControllerTypes() VirtualDeviceList {
|
||||||
|
// Return a mutable list of SCSI controller types, initialized with defaults.
|
||||||
|
return VirtualDeviceList([]types.BaseVirtualDevice{
|
||||||
|
&types.VirtualLsiLogicController{},
|
||||||
|
&types.VirtualBusLogicController{},
|
||||||
|
&types.ParaVirtualSCSIController{},
|
||||||
|
&types.VirtualLsiLogicSASController{},
|
||||||
|
}).Select(func(device types.BaseVirtualDevice) bool {
|
||||||
|
c := device.(types.BaseVirtualSCSIController).GetVirtualSCSIController()
|
||||||
|
c.SharedBus = types.VirtualSCSISharingNoSharing
|
||||||
|
c.BusNumber = -1
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// EthernetCardTypes are used for adding a new ethernet card to a VM.
|
||||||
|
func EthernetCardTypes() VirtualDeviceList {
|
||||||
|
return VirtualDeviceList([]types.BaseVirtualDevice{
|
||||||
|
&types.VirtualE1000{},
|
||||||
|
&types.VirtualE1000e{},
|
||||||
|
&types.VirtualVmxnet3{},
|
||||||
|
}).Select(func(device types.BaseVirtualDevice) bool {
|
||||||
|
c := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
|
||||||
|
c.GetVirtualDevice().Key = -1
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select returns a new list containing all elements of the list for which the given func returns true.
|
||||||
|
func (l VirtualDeviceList) Select(f func(device types.BaseVirtualDevice) bool) VirtualDeviceList {
|
||||||
|
var found VirtualDeviceList
|
||||||
|
|
||||||
|
for _, device := range l {
|
||||||
|
if f(device) {
|
||||||
|
found = append(found, device)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectByType returns a new list with devices that are equal to or extend the given type.
|
||||||
|
func (l VirtualDeviceList) SelectByType(deviceType types.BaseVirtualDevice) VirtualDeviceList {
|
||||||
|
dtype := reflect.TypeOf(deviceType)
|
||||||
|
if dtype == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
dname := dtype.Elem().Name()
|
||||||
|
|
||||||
|
return l.Select(func(device types.BaseVirtualDevice) bool {
|
||||||
|
t := reflect.TypeOf(device)
|
||||||
|
|
||||||
|
if t == dtype {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := t.Elem().FieldByName(dname)
|
||||||
|
|
||||||
|
return ok
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectByBackingInfo returns a new list with devices matching the given backing info.
|
||||||
|
// If the value of backing is nil, any device with a backing of the same type will be returned.
|
||||||
|
func (l VirtualDeviceList) SelectByBackingInfo(backing types.BaseVirtualDeviceBackingInfo) VirtualDeviceList {
|
||||||
|
t := reflect.TypeOf(backing)
|
||||||
|
|
||||||
|
return l.Select(func(device types.BaseVirtualDevice) bool {
|
||||||
|
db := device.GetVirtualDevice().Backing
|
||||||
|
if db == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.TypeOf(db) != t {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.ValueOf(backing).IsNil() {
|
||||||
|
// selecting by backing type
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch a := db.(type) {
|
||||||
|
case *types.VirtualEthernetCardNetworkBackingInfo:
|
||||||
|
b := backing.(*types.VirtualEthernetCardNetworkBackingInfo)
|
||||||
|
return a.DeviceName == b.DeviceName
|
||||||
|
case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo:
|
||||||
|
b := backing.(*types.VirtualEthernetCardDistributedVirtualPortBackingInfo)
|
||||||
|
return a.Port.SwitchUuid == b.Port.SwitchUuid &&
|
||||||
|
a.Port.PortgroupKey == b.Port.PortgroupKey
|
||||||
|
case *types.VirtualDiskFlatVer2BackingInfo:
|
||||||
|
b := backing.(*types.VirtualDiskFlatVer2BackingInfo)
|
||||||
|
if a.Parent != nil && b.Parent != nil {
|
||||||
|
return a.Parent.FileName == b.Parent.FileName
|
||||||
|
}
|
||||||
|
return a.FileName == b.FileName
|
||||||
|
case *types.VirtualSerialPortURIBackingInfo:
|
||||||
|
b := backing.(*types.VirtualSerialPortURIBackingInfo)
|
||||||
|
return a.ServiceURI == b.ServiceURI
|
||||||
|
case types.BaseVirtualDeviceFileBackingInfo:
|
||||||
|
b := backing.(types.BaseVirtualDeviceFileBackingInfo)
|
||||||
|
return a.GetVirtualDeviceFileBackingInfo().FileName == b.GetVirtualDeviceFileBackingInfo().FileName
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find returns the device matching the given name.
|
||||||
|
func (l VirtualDeviceList) Find(name string) types.BaseVirtualDevice {
|
||||||
|
for _, device := range l {
|
||||||
|
if l.Name(device) == name {
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindByKey returns the device matching the given key.
|
||||||
|
func (l VirtualDeviceList) FindByKey(key int32) types.BaseVirtualDevice {
|
||||||
|
for _, device := range l {
|
||||||
|
if device.GetVirtualDevice().Key == key {
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindIDEController will find the named IDE controller if given, otherwise will pick an available controller.
|
||||||
|
// An error is returned if the named controller is not found or not an IDE controller. Or, if name is not
|
||||||
|
// given and no available controller can be found.
|
||||||
|
func (l VirtualDeviceList) FindIDEController(name string) (*types.VirtualIDEController, error) {
|
||||||
|
if name != "" {
|
||||||
|
d := l.Find(name)
|
||||||
|
if d == nil {
|
||||||
|
return nil, fmt.Errorf("device '%s' not found", name)
|
||||||
|
}
|
||||||
|
if c, ok := d.(*types.VirtualIDEController); ok {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s is not an IDE controller", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := l.PickController((*types.VirtualIDEController)(nil))
|
||||||
|
if c == nil {
|
||||||
|
return nil, errors.New("no available IDE controller")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.(*types.VirtualIDEController), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateIDEController creates a new IDE controller.
|
||||||
|
func (l VirtualDeviceList) CreateIDEController() (types.BaseVirtualDevice, error) {
|
||||||
|
ide := &types.VirtualIDEController{}
|
||||||
|
ide.Key = l.NewKey()
|
||||||
|
return ide, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindSCSIController will find the named SCSI controller if given, otherwise will pick an available controller.
|
||||||
|
// An error is returned if the named controller is not found or not an SCSI controller. Or, if name is not
|
||||||
|
// given and no available controller can be found.
|
||||||
|
func (l VirtualDeviceList) FindSCSIController(name string) (*types.VirtualSCSIController, error) {
|
||||||
|
if name != "" {
|
||||||
|
d := l.Find(name)
|
||||||
|
if d == nil {
|
||||||
|
return nil, fmt.Errorf("device '%s' not found", name)
|
||||||
|
}
|
||||||
|
if c, ok := d.(types.BaseVirtualSCSIController); ok {
|
||||||
|
return c.GetVirtualSCSIController(), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s is not an SCSI controller", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := l.PickController((*types.VirtualSCSIController)(nil))
|
||||||
|
if c == nil {
|
||||||
|
return nil, errors.New("no available SCSI controller")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.(types.BaseVirtualSCSIController).GetVirtualSCSIController(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSCSIController creates a new SCSI controller of type name if given, otherwise defaults to lsilogic.
|
||||||
|
func (l VirtualDeviceList) CreateSCSIController(name string) (types.BaseVirtualDevice, error) {
|
||||||
|
ctypes := SCSIControllerTypes()
|
||||||
|
|
||||||
|
if name == "scsi" || name == "" {
|
||||||
|
name = ctypes.Type(ctypes[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
found := ctypes.Select(func(device types.BaseVirtualDevice) bool {
|
||||||
|
return l.Type(device) == name
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(found) == 0 {
|
||||||
|
return nil, fmt.Errorf("unknown SCSI controller type '%s'", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, ok := found[0].(types.BaseVirtualSCSIController)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("invalid SCSI controller type '%s'", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
scsi := c.GetVirtualSCSIController()
|
||||||
|
scsi.BusNumber = l.newSCSIBusNumber()
|
||||||
|
scsi.Key = l.NewKey()
|
||||||
|
scsi.ScsiCtlrUnitNumber = 7
|
||||||
|
return c.(types.BaseVirtualDevice), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var scsiBusNumbers = []int{0, 1, 2, 3}
|
||||||
|
|
||||||
|
// newSCSIBusNumber returns the bus number to use for adding a new SCSI bus device.
|
||||||
|
// -1 is returned if there are no bus numbers available.
|
||||||
|
func (l VirtualDeviceList) newSCSIBusNumber() int32 {
|
||||||
|
var used []int
|
||||||
|
|
||||||
|
for _, d := range l.SelectByType((*types.VirtualSCSIController)(nil)) {
|
||||||
|
num := d.(types.BaseVirtualSCSIController).GetVirtualSCSIController().BusNumber
|
||||||
|
if num >= 0 {
|
||||||
|
used = append(used, int(num))
|
||||||
|
} // else caller is creating a new vm using SCSIControllerTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Ints(used)
|
||||||
|
|
||||||
|
for i, n := range scsiBusNumbers {
|
||||||
|
if i == len(used) || n != used[i] {
|
||||||
|
return int32(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindNVMEController will find the named NVME controller if given, otherwise will pick an available controller.
|
||||||
|
// An error is returned if the named controller is not found or not an NVME controller. Or, if name is not
|
||||||
|
// given and no available controller can be found.
|
||||||
|
func (l VirtualDeviceList) FindNVMEController(name string) (*types.VirtualNVMEController, error) {
|
||||||
|
if name != "" {
|
||||||
|
d := l.Find(name)
|
||||||
|
if d == nil {
|
||||||
|
return nil, fmt.Errorf("device '%s' not found", name)
|
||||||
|
}
|
||||||
|
if c, ok := d.(*types.VirtualNVMEController); ok {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s is not an NVME controller", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := l.PickController((*types.VirtualNVMEController)(nil))
|
||||||
|
if c == nil {
|
||||||
|
return nil, errors.New("no available NVME controller")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.(*types.VirtualNVMEController), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateNVMEController creates a new NVMWE controller.
|
||||||
|
func (l VirtualDeviceList) CreateNVMEController() (types.BaseVirtualDevice, error) {
|
||||||
|
nvme := &types.VirtualNVMEController{}
|
||||||
|
nvme.BusNumber = l.newNVMEBusNumber()
|
||||||
|
nvme.Key = l.NewKey()
|
||||||
|
|
||||||
|
return nvme, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var nvmeBusNumbers = []int{0, 1, 2, 3}
|
||||||
|
|
||||||
|
// newNVMEBusNumber returns the bus number to use for adding a new NVME bus device.
|
||||||
|
// -1 is returned if there are no bus numbers available.
|
||||||
|
func (l VirtualDeviceList) newNVMEBusNumber() int32 {
|
||||||
|
var used []int
|
||||||
|
|
||||||
|
for _, d := range l.SelectByType((*types.VirtualNVMEController)(nil)) {
|
||||||
|
num := d.(types.BaseVirtualController).GetVirtualController().BusNumber
|
||||||
|
if num >= 0 {
|
||||||
|
used = append(used, int(num))
|
||||||
|
} // else caller is creating a new vm using NVMEControllerTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Ints(used)
|
||||||
|
|
||||||
|
for i, n := range nvmeBusNumbers {
|
||||||
|
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 {
|
||||||
|
case name == "ide":
|
||||||
|
return l.FindIDEController("")
|
||||||
|
case name == "scsi" || name == "":
|
||||||
|
return l.FindSCSIController("")
|
||||||
|
case name == "nvme":
|
||||||
|
return l.FindNVMEController("")
|
||||||
|
default:
|
||||||
|
if c, ok := l.Find(name).(types.BaseVirtualController); ok {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s is not a valid controller", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PickController returns a controller of the given type(s).
|
||||||
|
// If no controllers are found or have no available slots, then nil is returned.
|
||||||
|
func (l VirtualDeviceList) PickController(kind types.BaseVirtualController) types.BaseVirtualController {
|
||||||
|
l = l.SelectByType(kind.(types.BaseVirtualDevice)).Select(func(device types.BaseVirtualDevice) bool {
|
||||||
|
num := len(device.(types.BaseVirtualController).GetVirtualController().Device)
|
||||||
|
|
||||||
|
switch device.(type) {
|
||||||
|
case types.BaseVirtualSCSIController:
|
||||||
|
return num < 15
|
||||||
|
case *types.VirtualIDEController:
|
||||||
|
return num < 2
|
||||||
|
case *types.VirtualNVMEController:
|
||||||
|
return num < 8
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(l) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return l[0].(types.BaseVirtualController)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newUnitNumber returns the unit number to use for attaching a new device to the given controller.
|
||||||
|
func (l VirtualDeviceList) newUnitNumber(c types.BaseVirtualController) int32 {
|
||||||
|
units := make([]bool, 30)
|
||||||
|
|
||||||
|
switch sc := c.(type) {
|
||||||
|
case types.BaseVirtualSCSIController:
|
||||||
|
// The SCSI controller sits on its own bus
|
||||||
|
units[sc.GetVirtualSCSIController().ScsiCtlrUnitNumber] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
key := c.GetVirtualController().Key
|
||||||
|
|
||||||
|
for _, device := range l {
|
||||||
|
d := device.GetVirtualDevice()
|
||||||
|
|
||||||
|
if d.ControllerKey == key && d.UnitNumber != nil {
|
||||||
|
units[int(*d.UnitNumber)] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for unit, used := range units {
|
||||||
|
if !used {
|
||||||
|
return int32(unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKey returns the key to use for adding a new device to the device list.
|
||||||
|
// The device list we're working with here may not be complete (e.g. when
|
||||||
|
// we're only adding new devices), so any positive keys could conflict with device keys
|
||||||
|
// that are already in use. To avoid this type of conflict, we can use negative keys
|
||||||
|
// here, which will be resolved to positive keys by vSphere as the reconfiguration is done.
|
||||||
|
func (l VirtualDeviceList) NewKey() int32 {
|
||||||
|
var key int32 = -200
|
||||||
|
|
||||||
|
for _, device := range l {
|
||||||
|
d := device.GetVirtualDevice()
|
||||||
|
if d.Key < key {
|
||||||
|
key = d.Key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssignController assigns a device to a controller.
|
||||||
|
func (l VirtualDeviceList) AssignController(device types.BaseVirtualDevice, c types.BaseVirtualController) {
|
||||||
|
d := device.GetVirtualDevice()
|
||||||
|
d.ControllerKey = c.GetVirtualController().Key
|
||||||
|
d.UnitNumber = new(int32)
|
||||||
|
*d.UnitNumber = l.newUnitNumber(c)
|
||||||
|
if d.Key == 0 {
|
||||||
|
d.Key = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDisk creates a new VirtualDisk device which can be added to a VM.
|
||||||
|
func (l VirtualDeviceList) CreateDisk(c types.BaseVirtualController, ds types.ManagedObjectReference, name string) *types.VirtualDisk {
|
||||||
|
// If name is not specified, one will be chosen for you.
|
||||||
|
// But if when given, make sure it ends in .vmdk, otherwise it will be treated as a directory.
|
||||||
|
if len(name) > 0 && filepath.Ext(name) != ".vmdk" {
|
||||||
|
name += ".vmdk"
|
||||||
|
}
|
||||||
|
|
||||||
|
device := &types.VirtualDisk{
|
||||||
|
VirtualDevice: types.VirtualDevice{
|
||||||
|
Backing: &types.VirtualDiskFlatVer2BackingInfo{
|
||||||
|
DiskMode: string(types.VirtualDiskModePersistent),
|
||||||
|
ThinProvisioned: types.NewBool(true),
|
||||||
|
VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
|
||||||
|
FileName: name,
|
||||||
|
Datastore: &ds,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
l.AssignController(device, c)
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChildDisk creates a new VirtualDisk device, linked to the given parent disk, which can be added to a VM.
|
||||||
|
func (l VirtualDeviceList) ChildDisk(parent *types.VirtualDisk) *types.VirtualDisk {
|
||||||
|
disk := *parent
|
||||||
|
backing := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
|
||||||
|
p := new(DatastorePath)
|
||||||
|
p.FromString(backing.FileName)
|
||||||
|
p.Path = ""
|
||||||
|
|
||||||
|
// Use specified disk as parent backing to a new disk.
|
||||||
|
disk.Backing = &types.VirtualDiskFlatVer2BackingInfo{
|
||||||
|
VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
|
||||||
|
FileName: p.String(),
|
||||||
|
Datastore: backing.Datastore,
|
||||||
|
},
|
||||||
|
Parent: backing,
|
||||||
|
DiskMode: backing.DiskMode,
|
||||||
|
ThinProvisioned: backing.ThinProvisioned,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &disk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l VirtualDeviceList) connectivity(device types.BaseVirtualDevice, v bool) error {
|
||||||
|
c := device.GetVirtualDevice().Connectable
|
||||||
|
if c == nil {
|
||||||
|
return fmt.Errorf("%s is not connectable", l.Name(device))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Connected = v
|
||||||
|
c.StartConnected = v
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect changes the device to connected, returns an error if the device is not connectable.
|
||||||
|
func (l VirtualDeviceList) Connect(device types.BaseVirtualDevice) error {
|
||||||
|
return l.connectivity(device, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect changes the device to disconnected, returns an error if the device is not connectable.
|
||||||
|
func (l VirtualDeviceList) Disconnect(device types.BaseVirtualDevice) error {
|
||||||
|
return l.connectivity(device, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindCdrom finds a cdrom device with the given name, defaulting to the first cdrom device if any.
|
||||||
|
func (l VirtualDeviceList) FindCdrom(name string) (*types.VirtualCdrom, error) {
|
||||||
|
if name != "" {
|
||||||
|
d := l.Find(name)
|
||||||
|
if d == nil {
|
||||||
|
return nil, fmt.Errorf("device '%s' not found", name)
|
||||||
|
}
|
||||||
|
if c, ok := d.(*types.VirtualCdrom); ok {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s is not a cdrom device", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := l.SelectByType((*types.VirtualCdrom)(nil))
|
||||||
|
if len(c) == 0 {
|
||||||
|
return nil, errors.New("no cdrom device found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c[0].(*types.VirtualCdrom), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCdrom creates a new VirtualCdrom device which can be added to a VM.
|
||||||
|
func (l VirtualDeviceList) CreateCdrom(c *types.VirtualIDEController) (*types.VirtualCdrom, error) {
|
||||||
|
device := &types.VirtualCdrom{}
|
||||||
|
|
||||||
|
l.AssignController(device, c)
|
||||||
|
|
||||||
|
l.setDefaultCdromBacking(device)
|
||||||
|
|
||||||
|
device.Connectable = &types.VirtualDeviceConnectInfo{
|
||||||
|
AllowGuestControl: true,
|
||||||
|
Connected: true,
|
||||||
|
StartConnected: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return device, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertIso changes the cdrom device backing to use the given iso file.
|
||||||
|
func (l VirtualDeviceList) InsertIso(device *types.VirtualCdrom, iso string) *types.VirtualCdrom {
|
||||||
|
device.Backing = &types.VirtualCdromIsoBackingInfo{
|
||||||
|
VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
|
||||||
|
FileName: iso,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
|
||||||
|
// EjectIso removes the iso file based backing and replaces with the default cdrom backing.
|
||||||
|
func (l VirtualDeviceList) EjectIso(device *types.VirtualCdrom) *types.VirtualCdrom {
|
||||||
|
l.setDefaultCdromBacking(device)
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l VirtualDeviceList) setDefaultCdromBacking(device *types.VirtualCdrom) {
|
||||||
|
device.Backing = &types.VirtualCdromAtapiBackingInfo{
|
||||||
|
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
|
||||||
|
DeviceName: fmt.Sprintf("%s-%d-%d", DeviceTypeCdrom, device.ControllerKey, device.UnitNumber),
|
||||||
|
UseAutoDetect: types.NewBool(false),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFloppy finds a floppy device with the given name, defaulting to the first floppy device if any.
|
||||||
|
func (l VirtualDeviceList) FindFloppy(name string) (*types.VirtualFloppy, error) {
|
||||||
|
if name != "" {
|
||||||
|
d := l.Find(name)
|
||||||
|
if d == nil {
|
||||||
|
return nil, fmt.Errorf("device '%s' not found", name)
|
||||||
|
}
|
||||||
|
if c, ok := d.(*types.VirtualFloppy); ok {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s is not a floppy device", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := l.SelectByType((*types.VirtualFloppy)(nil))
|
||||||
|
if len(c) == 0 {
|
||||||
|
return nil, errors.New("no floppy device found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c[0].(*types.VirtualFloppy), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFloppy creates a new VirtualFloppy device which can be added to a VM.
|
||||||
|
func (l VirtualDeviceList) CreateFloppy() (*types.VirtualFloppy, error) {
|
||||||
|
device := &types.VirtualFloppy{}
|
||||||
|
|
||||||
|
c := l.PickController((*types.VirtualSIOController)(nil))
|
||||||
|
if c == nil {
|
||||||
|
return nil, errors.New("no available SIO controller")
|
||||||
|
}
|
||||||
|
|
||||||
|
l.AssignController(device, c)
|
||||||
|
|
||||||
|
l.setDefaultFloppyBacking(device)
|
||||||
|
|
||||||
|
device.Connectable = &types.VirtualDeviceConnectInfo{
|
||||||
|
AllowGuestControl: true,
|
||||||
|
Connected: true,
|
||||||
|
StartConnected: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return device, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertImg changes the floppy device backing to use the given img file.
|
||||||
|
func (l VirtualDeviceList) InsertImg(device *types.VirtualFloppy, img string) *types.VirtualFloppy {
|
||||||
|
device.Backing = &types.VirtualFloppyImageBackingInfo{
|
||||||
|
VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
|
||||||
|
FileName: img,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
|
||||||
|
// EjectImg removes the img file based backing and replaces with the default floppy backing.
|
||||||
|
func (l VirtualDeviceList) EjectImg(device *types.VirtualFloppy) *types.VirtualFloppy {
|
||||||
|
l.setDefaultFloppyBacking(device)
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l VirtualDeviceList) setDefaultFloppyBacking(device *types.VirtualFloppy) {
|
||||||
|
device.Backing = &types.VirtualFloppyDeviceBackingInfo{
|
||||||
|
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
|
||||||
|
DeviceName: fmt.Sprintf("%s-%d", DeviceTypeFloppy, device.UnitNumber),
|
||||||
|
UseAutoDetect: types.NewBool(false),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindSerialPort finds a serial port device with the given name, defaulting to the first serial port device if any.
|
||||||
|
func (l VirtualDeviceList) FindSerialPort(name string) (*types.VirtualSerialPort, error) {
|
||||||
|
if name != "" {
|
||||||
|
d := l.Find(name)
|
||||||
|
if d == nil {
|
||||||
|
return nil, fmt.Errorf("device '%s' not found", name)
|
||||||
|
}
|
||||||
|
if c, ok := d.(*types.VirtualSerialPort); ok {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s is not a serial port device", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := l.SelectByType((*types.VirtualSerialPort)(nil))
|
||||||
|
if len(c) == 0 {
|
||||||
|
return nil, errors.New("no serial port device found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c[0].(*types.VirtualSerialPort), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSerialPort creates a new VirtualSerialPort device which can be added to a VM.
|
||||||
|
func (l VirtualDeviceList) CreateSerialPort() (*types.VirtualSerialPort, error) {
|
||||||
|
device := &types.VirtualSerialPort{
|
||||||
|
YieldOnPoll: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
c := l.PickController((*types.VirtualSIOController)(nil))
|
||||||
|
if c == nil {
|
||||||
|
return nil, errors.New("no available SIO controller")
|
||||||
|
}
|
||||||
|
|
||||||
|
l.AssignController(device, c)
|
||||||
|
|
||||||
|
l.setDefaultSerialPortBacking(device)
|
||||||
|
|
||||||
|
return device, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectSerialPort connects a serial port to a server or client uri.
|
||||||
|
func (l VirtualDeviceList) ConnectSerialPort(device *types.VirtualSerialPort, uri string, client bool, proxyuri string) *types.VirtualSerialPort {
|
||||||
|
if strings.HasPrefix(uri, "[") {
|
||||||
|
device.Backing = &types.VirtualSerialPortFileBackingInfo{
|
||||||
|
VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
|
||||||
|
FileName: uri,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
|
||||||
|
direction := types.VirtualDeviceURIBackingOptionDirectionServer
|
||||||
|
if client {
|
||||||
|
direction = types.VirtualDeviceURIBackingOptionDirectionClient
|
||||||
|
}
|
||||||
|
|
||||||
|
device.Backing = &types.VirtualSerialPortURIBackingInfo{
|
||||||
|
VirtualDeviceURIBackingInfo: types.VirtualDeviceURIBackingInfo{
|
||||||
|
Direction: string(direction),
|
||||||
|
ServiceURI: uri,
|
||||||
|
ProxyURI: proxyuri,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisconnectSerialPort disconnects the serial port backing.
|
||||||
|
func (l VirtualDeviceList) DisconnectSerialPort(device *types.VirtualSerialPort) *types.VirtualSerialPort {
|
||||||
|
l.setDefaultSerialPortBacking(device)
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l VirtualDeviceList) setDefaultSerialPortBacking(device *types.VirtualSerialPort) {
|
||||||
|
device.Backing = &types.VirtualSerialPortURIBackingInfo{
|
||||||
|
VirtualDeviceURIBackingInfo: types.VirtualDeviceURIBackingInfo{
|
||||||
|
Direction: "client",
|
||||||
|
ServiceURI: "localhost:0",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateEthernetCard creates a new VirtualEthernetCard of the given name name and initialized with the given backing.
|
||||||
|
func (l VirtualDeviceList) CreateEthernetCard(name string, backing types.BaseVirtualDeviceBackingInfo) (types.BaseVirtualDevice, error) {
|
||||||
|
ctypes := EthernetCardTypes()
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
name = ctypes.deviceName(ctypes[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
found := ctypes.Select(func(device types.BaseVirtualDevice) bool {
|
||||||
|
return l.deviceName(device) == name
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(found) == 0 {
|
||||||
|
return nil, fmt.Errorf("unknown ethernet card type '%s'", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, ok := found[0].(types.BaseVirtualEthernetCard)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("invalid ethernet card type '%s'", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.GetVirtualEthernetCard().Backing = backing
|
||||||
|
|
||||||
|
return c.(types.BaseVirtualDevice), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrimaryMacAddress returns the MacAddress field of the primary VirtualEthernetCard
|
||||||
|
func (l VirtualDeviceList) PrimaryMacAddress() string {
|
||||||
|
eth0 := l.Find("ethernet-0")
|
||||||
|
|
||||||
|
if eth0 == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return eth0.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard().MacAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert a BaseVirtualDevice to a BaseVirtualMachineBootOptionsBootableDevice
|
||||||
|
var bootableDevices = map[string]func(device types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice{
|
||||||
|
DeviceTypeCdrom: func(types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
|
||||||
|
return &types.VirtualMachineBootOptionsBootableCdromDevice{}
|
||||||
|
},
|
||||||
|
DeviceTypeDisk: func(d types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
|
||||||
|
return &types.VirtualMachineBootOptionsBootableDiskDevice{
|
||||||
|
DeviceKey: d.GetVirtualDevice().Key,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DeviceTypeEthernet: func(d types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
|
||||||
|
return &types.VirtualMachineBootOptionsBootableEthernetDevice{
|
||||||
|
DeviceKey: d.GetVirtualDevice().Key,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DeviceTypeFloppy: func(types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
|
||||||
|
return &types.VirtualMachineBootOptionsBootableFloppyDevice{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// BootOrder returns a list of devices which can be used to set boot order via VirtualMachine.SetBootOptions.
|
||||||
|
// The order can any of "ethernet", "cdrom", "floppy" or "disk" or by specific device name.
|
||||||
|
func (l VirtualDeviceList) BootOrder(order []string) []types.BaseVirtualMachineBootOptionsBootableDevice {
|
||||||
|
var devices []types.BaseVirtualMachineBootOptionsBootableDevice
|
||||||
|
|
||||||
|
for _, name := range order {
|
||||||
|
if kind, ok := bootableDevices[name]; ok {
|
||||||
|
for _, device := range l {
|
||||||
|
if l.Type(device) == name {
|
||||||
|
devices = append(devices, kind(device))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if d := l.Find(name); d != nil {
|
||||||
|
if kind, ok := bootableDevices[l.Type(d)]; ok {
|
||||||
|
devices = append(devices, kind(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return devices
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectBootOrder returns an ordered list of devices matching the given bootable device order
|
||||||
|
func (l VirtualDeviceList) SelectBootOrder(order []types.BaseVirtualMachineBootOptionsBootableDevice) VirtualDeviceList {
|
||||||
|
var devices VirtualDeviceList
|
||||||
|
|
||||||
|
for _, bd := range order {
|
||||||
|
for _, device := range l {
|
||||||
|
if kind, ok := bootableDevices[l.Type(device)]; ok {
|
||||||
|
if reflect.DeepEqual(kind(device), bd) {
|
||||||
|
devices = append(devices, device)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return devices
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeName returns the vmodl type name of the device
|
||||||
|
func (l VirtualDeviceList) TypeName(device types.BaseVirtualDevice) string {
|
||||||
|
dtype := reflect.TypeOf(device)
|
||||||
|
if dtype == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return dtype.Elem().Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
var deviceNameRegexp = regexp.MustCompile(`(?:Virtual)?(?:Machine)?(\w+?)(?:Card|Device|Controller)?$`)
|
||||||
|
|
||||||
|
func (l VirtualDeviceList) deviceName(device types.BaseVirtualDevice) string {
|
||||||
|
name := "device"
|
||||||
|
typeName := l.TypeName(device)
|
||||||
|
|
||||||
|
m := deviceNameRegexp.FindStringSubmatch(typeName)
|
||||||
|
if len(m) == 2 {
|
||||||
|
name = strings.ToLower(m[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns a human-readable name for the given device
|
||||||
|
func (l VirtualDeviceList) Type(device types.BaseVirtualDevice) string {
|
||||||
|
switch device.(type) {
|
||||||
|
case types.BaseVirtualEthernetCard:
|
||||||
|
return DeviceTypeEthernet
|
||||||
|
case *types.ParaVirtualSCSIController:
|
||||||
|
return "pvscsi"
|
||||||
|
case *types.VirtualLsiLogicSASController:
|
||||||
|
return "lsilogic-sas"
|
||||||
|
case *types.VirtualNVMEController:
|
||||||
|
return "nvme"
|
||||||
|
default:
|
||||||
|
return l.deviceName(device)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns a stable, human-readable name for the given device
|
||||||
|
func (l VirtualDeviceList) Name(device types.BaseVirtualDevice) string {
|
||||||
|
var key string
|
||||||
|
var UnitNumber int32
|
||||||
|
d := device.GetVirtualDevice()
|
||||||
|
if d.UnitNumber != nil {
|
||||||
|
UnitNumber = *d.UnitNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
dtype := l.Type(device)
|
||||||
|
switch dtype {
|
||||||
|
case DeviceTypeEthernet:
|
||||||
|
key = fmt.Sprintf("%d", UnitNumber-7)
|
||||||
|
case DeviceTypeDisk:
|
||||||
|
key = fmt.Sprintf("%d-%d", d.ControllerKey, UnitNumber)
|
||||||
|
default:
|
||||||
|
key = fmt.Sprintf("%d", d.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s-%s", dtype, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigSpec creates a virtual machine configuration spec for
|
||||||
|
// the specified operation, for the list of devices in the device list.
|
||||||
|
func (l VirtualDeviceList) ConfigSpec(op types.VirtualDeviceConfigSpecOperation) ([]types.BaseVirtualDeviceConfigSpec, error) {
|
||||||
|
var fop types.VirtualDeviceConfigSpecFileOperation
|
||||||
|
switch op {
|
||||||
|
case types.VirtualDeviceConfigSpecOperationAdd:
|
||||||
|
fop = types.VirtualDeviceConfigSpecFileOperationCreate
|
||||||
|
case types.VirtualDeviceConfigSpecOperationEdit:
|
||||||
|
fop = types.VirtualDeviceConfigSpecFileOperationReplace
|
||||||
|
case types.VirtualDeviceConfigSpecOperationRemove:
|
||||||
|
fop = types.VirtualDeviceConfigSpecFileOperationDestroy
|
||||||
|
default:
|
||||||
|
panic("unknown op")
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []types.BaseVirtualDeviceConfigSpec
|
||||||
|
for _, device := range l {
|
||||||
|
config := &types.VirtualDeviceConfigSpec{
|
||||||
|
Device: device,
|
||||||
|
Operation: op,
|
||||||
|
}
|
||||||
|
|
||||||
|
if disk, ok := device.(*types.VirtualDisk); ok {
|
||||||
|
config.FileOperation = fop
|
||||||
|
|
||||||
|
// Special case to attach an existing disk
|
||||||
|
if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 {
|
||||||
|
childDisk := false
|
||||||
|
if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
|
||||||
|
childDisk = b.Parent != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !childDisk {
|
||||||
|
// Existing disk, clear file operation
|
||||||
|
config.FileOperation = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VirtualDiskManager struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVirtualDiskManager(c *vim25.Client) *VirtualDiskManager {
|
||||||
|
m := VirtualDiskManager{
|
||||||
|
Common: NewCommon(c, *c.ServiceContent.VirtualDiskManager),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyVirtualDisk copies a virtual disk, performing conversions as specified in the spec.
|
||||||
|
func (m VirtualDiskManager) CopyVirtualDisk(
|
||||||
|
ctx context.Context,
|
||||||
|
sourceName string, sourceDatacenter *Datacenter,
|
||||||
|
destName string, destDatacenter *Datacenter,
|
||||||
|
destSpec *types.VirtualDiskSpec, force bool) (*Task, error) {
|
||||||
|
|
||||||
|
req := types.CopyVirtualDisk_Task{
|
||||||
|
This: m.Reference(),
|
||||||
|
SourceName: sourceName,
|
||||||
|
DestName: destName,
|
||||||
|
DestSpec: destSpec,
|
||||||
|
Force: types.NewBool(force),
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceDatacenter != nil {
|
||||||
|
ref := sourceDatacenter.Reference()
|
||||||
|
req.SourceDatacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
if destDatacenter != nil {
|
||||||
|
ref := destDatacenter.Reference()
|
||||||
|
req.DestDatacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CopyVirtualDisk_Task(ctx, m.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(m.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateVirtualDisk creates a new virtual disk.
|
||||||
|
func (m VirtualDiskManager) CreateVirtualDisk(
|
||||||
|
ctx context.Context,
|
||||||
|
name string, datacenter *Datacenter,
|
||||||
|
spec types.BaseVirtualDiskSpec) (*Task, error) {
|
||||||
|
|
||||||
|
req := types.CreateVirtualDisk_Task{
|
||||||
|
This: m.Reference(),
|
||||||
|
Name: name,
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
if datacenter != nil {
|
||||||
|
ref := datacenter.Reference()
|
||||||
|
req.Datacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateVirtualDisk_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,
|
||||||
|
sourceName string, sourceDatacenter *Datacenter,
|
||||||
|
destName string, destDatacenter *Datacenter,
|
||||||
|
force bool) (*Task, error) {
|
||||||
|
req := types.MoveVirtualDisk_Task{
|
||||||
|
This: m.Reference(),
|
||||||
|
SourceName: sourceName,
|
||||||
|
DestName: destName,
|
||||||
|
Force: types.NewBool(force),
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceDatacenter != nil {
|
||||||
|
ref := sourceDatacenter.Reference()
|
||||||
|
req.SourceDatacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
if destDatacenter != nil {
|
||||||
|
ref := destDatacenter.Reference()
|
||||||
|
req.DestDatacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.MoveVirtualDisk_Task(ctx, m.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(m.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteVirtualDisk deletes a virtual disk.
|
||||||
|
func (m VirtualDiskManager) DeleteVirtualDisk(ctx context.Context, name string, dc *Datacenter) (*Task, error) {
|
||||||
|
req := types.DeleteVirtualDisk_Task{
|
||||||
|
This: m.Reference(),
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
if dc != nil {
|
||||||
|
ref := dc.Reference()
|
||||||
|
req.Datacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.DeleteVirtualDisk_Task(ctx, m.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(m.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queries virtual disk uuid
|
||||||
|
func (m VirtualDiskManager) QueryVirtualDiskUuid(ctx context.Context, name string, dc *Datacenter) (string, error) {
|
||||||
|
req := types.QueryVirtualDiskUuid{
|
||||||
|
This: m.Reference(),
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
if dc != nil {
|
||||||
|
ref := dc.Reference()
|
||||||
|
req.Datacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.QueryVirtualDiskUuid(ctx, m.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
97
vendor/github.com/vmware/govmomi/object/virtual_disk_manager_internal.go
generated
vendored
Normal file
97
vendor/github.com/vmware/govmomi/object/virtual_disk_manager_internal.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 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"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
types.Add("ArrayOfVirtualDiskInfo", reflect.TypeOf((*arrayOfVirtualDiskInfo)(nil)).Elem())
|
||||||
|
|
||||||
|
types.Add("VirtualDiskInfo", reflect.TypeOf((*VirtualDiskInfo)(nil)).Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
type arrayOfVirtualDiskInfo struct {
|
||||||
|
VirtualDiskInfo []VirtualDiskInfo `xml:"VirtualDiskInfo,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type queryVirtualDiskInfoTaskRequest struct {
|
||||||
|
This types.ManagedObjectReference `xml:"_this"`
|
||||||
|
Name string `xml:"name"`
|
||||||
|
Datacenter *types.ManagedObjectReference `xml:"datacenter,omitempty"`
|
||||||
|
IncludeParents bool `xml:"includeParents"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type queryVirtualDiskInfoTaskResponse struct {
|
||||||
|
Returnval types.ManagedObjectReference `xml:"returnval"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type queryVirtualDiskInfoTaskBody struct {
|
||||||
|
Req *queryVirtualDiskInfoTaskRequest `xml:"urn:internalvim25 QueryVirtualDiskInfo_Task,omitempty"`
|
||||||
|
Res *queryVirtualDiskInfoTaskResponse `xml:"urn:vim25 QueryVirtualDiskInfo_TaskResponse,omitempty"`
|
||||||
|
Err *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *queryVirtualDiskInfoTaskBody) Fault() *soap.Fault { return b.Err }
|
||||||
|
|
||||||
|
func queryVirtualDiskInfoTask(ctx context.Context, r soap.RoundTripper, req *queryVirtualDiskInfoTaskRequest) (*queryVirtualDiskInfoTaskResponse, error) {
|
||||||
|
var reqBody, resBody queryVirtualDiskInfoTaskBody
|
||||||
|
|
||||||
|
reqBody.Req = req
|
||||||
|
|
||||||
|
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resBody.Res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type VirtualDiskInfo struct {
|
||||||
|
Name string `xml:"unit>name"`
|
||||||
|
DiskType string `xml:"diskType"`
|
||||||
|
Parent string `xml:"parent,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m VirtualDiskManager) QueryVirtualDiskInfo(ctx context.Context, name string, dc *Datacenter, includeParents bool) ([]VirtualDiskInfo, error) {
|
||||||
|
req := queryVirtualDiskInfoTaskRequest{
|
||||||
|
This: m.Reference(),
|
||||||
|
Name: name,
|
||||||
|
IncludeParents: includeParents,
|
||||||
|
}
|
||||||
|
|
||||||
|
if dc != nil {
|
||||||
|
ref := dc.Reference()
|
||||||
|
req.Datacenter = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := queryVirtualDiskInfoTask(ctx, m.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := NewTask(m.Client(), res.Returnval).WaitForResult(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.Result.(arrayOfVirtualDiskInfo).VirtualDiskInfo, nil
|
||||||
|
}
|
|
@ -0,0 +1,759 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015-2017 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"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/property"
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PropRuntimePowerState = "summary.runtime.powerState"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VirtualMachine struct {
|
||||||
|
Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVirtualMachine(c *vim25.Client, ref types.ManagedObjectReference) *VirtualMachine {
|
||||||
|
return &VirtualMachine{
|
||||||
|
Common: NewCommon(c, ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) PowerState(ctx context.Context) (types.VirtualMachinePowerState, error) {
|
||||||
|
var o mo.VirtualMachine
|
||||||
|
|
||||||
|
err := v.Properties(ctx, v.Reference(), []string{PropRuntimePowerState}, &o)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return o.Summary.Runtime.PowerState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) PowerOn(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.PowerOnVM_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.PowerOnVM_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) PowerOff(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.PowerOffVM_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.PowerOffVM_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) Reset(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.ResetVM_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ResetVM_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) Suspend(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.SuspendVM_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.SuspendVM_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) ShutdownGuest(ctx context.Context) error {
|
||||||
|
req := types.ShutdownGuest{
|
||||||
|
This: v.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.ShutdownGuest(ctx, v.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) RebootGuest(ctx context.Context) error {
|
||||||
|
req := types.RebootGuest{
|
||||||
|
This: v.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.RebootGuest(ctx, v.c, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) Destroy(ctx context.Context) (*Task, error) {
|
||||||
|
req := types.Destroy_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.Destroy_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) Clone(ctx context.Context, folder *Folder, name string, config types.VirtualMachineCloneSpec) (*Task, error) {
|
||||||
|
req := types.CloneVM_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
Folder: folder.Reference(),
|
||||||
|
Name: name,
|
||||||
|
Spec: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CloneVM_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) Customize(ctx context.Context, spec types.CustomizationSpec) (*Task, error) {
|
||||||
|
req := types.CustomizeVM_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CustomizeVM_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) Relocate(ctx context.Context, config types.VirtualMachineRelocateSpec, priority types.VirtualMachineMovePriority) (*Task, error) {
|
||||||
|
req := types.RelocateVM_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
Spec: config,
|
||||||
|
Priority: priority,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.RelocateVM_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) Reconfigure(ctx context.Context, config types.VirtualMachineConfigSpec) (*Task, error) {
|
||||||
|
req := types.ReconfigVM_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
Spec: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.ReconfigVM_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) WaitForIP(ctx context.Context) (string, error) {
|
||||||
|
var ip string
|
||||||
|
|
||||||
|
p := property.DefaultCollector(v.c)
|
||||||
|
err := property.Wait(ctx, p, v.Reference(), []string{"guest.ipAddress"}, func(pc []types.PropertyChange) bool {
|
||||||
|
for _, c := range pc {
|
||||||
|
if c.Name != "guest.ipAddress" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c.Op != types.PropertyChangeOpAssign {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c.Val == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = c.Val.(string)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForNetIP waits for the VM guest.net property to report an IP address for all VM NICs.
|
||||||
|
// Only consider IPv4 addresses if the v4 param is true.
|
||||||
|
// By default, wait for all NICs to get an IP address, unless 1 or more device is given.
|
||||||
|
// A device can be specified by the MAC address or the device name, e.g. "ethernet-0".
|
||||||
|
// Returns a map with MAC address as the key and IP address list as the value.
|
||||||
|
func (v VirtualMachine) WaitForNetIP(ctx context.Context, v4 bool, device ...string) (map[string][]string, error) {
|
||||||
|
macs := make(map[string][]string)
|
||||||
|
eths := make(map[string]string)
|
||||||
|
|
||||||
|
p := property.DefaultCollector(v.c)
|
||||||
|
|
||||||
|
// Wait for all NICs to have a MacAddress, which may not be generated yet.
|
||||||
|
err := property.Wait(ctx, p, v.Reference(), []string{"config.hardware.device"}, func(pc []types.PropertyChange) bool {
|
||||||
|
for _, c := range pc {
|
||||||
|
if c.Op != types.PropertyChangeOpAssign {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
devices := VirtualDeviceList(c.Val.(types.ArrayOfVirtualDevice).VirtualDevice)
|
||||||
|
for _, d := range devices {
|
||||||
|
if nic, ok := d.(types.BaseVirtualEthernetCard); ok {
|
||||||
|
mac := nic.GetVirtualEthernetCard().MacAddress
|
||||||
|
if mac == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
macs[mac] = nil
|
||||||
|
eths[devices.Name(d)] = mac
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(device) != 0 {
|
||||||
|
// Only wait for specific NIC(s)
|
||||||
|
macs = make(map[string][]string)
|
||||||
|
for _, mac := range device {
|
||||||
|
if eth, ok := eths[mac]; ok {
|
||||||
|
mac = eth // device name, e.g. "ethernet-0"
|
||||||
|
}
|
||||||
|
macs[mac] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = property.Wait(ctx, p, v.Reference(), []string{"guest.net"}, func(pc []types.PropertyChange) bool {
|
||||||
|
for _, c := range pc {
|
||||||
|
if c.Op != types.PropertyChangeOpAssign {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nics := c.Val.(types.ArrayOfGuestNicInfo).GuestNicInfo
|
||||||
|
for _, nic := range nics {
|
||||||
|
mac := nic.MacAddress
|
||||||
|
if mac == "" || nic.IpConfig == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range nic.IpConfig.IpAddress {
|
||||||
|
if _, ok := macs[mac]; !ok {
|
||||||
|
continue // Ignore any that don't correspond to a VM device
|
||||||
|
}
|
||||||
|
if v4 && net.ParseIP(ip.IpAddress).To4() == nil {
|
||||||
|
continue // Ignore non IPv4 address
|
||||||
|
}
|
||||||
|
macs[mac] = append(macs[mac], ip.IpAddress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ips := range macs {
|
||||||
|
if len(ips) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return macs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device returns the VirtualMachine's config.hardware.device property.
|
||||||
|
func (v VirtualMachine) Device(ctx context.Context) (VirtualDeviceList, error) {
|
||||||
|
var o mo.VirtualMachine
|
||||||
|
|
||||||
|
err := v.Properties(ctx, v.Reference(), []string{"config.hardware.device", "summary.runtime.connectionState"}, &o)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quoting the SDK doc:
|
||||||
|
// The virtual machine configuration is not guaranteed to be available.
|
||||||
|
// For example, the configuration information would be unavailable if the server
|
||||||
|
// is unable to access the virtual machine files on disk, and is often also unavailable
|
||||||
|
// during the initial phases of virtual machine creation.
|
||||||
|
if o.Config == nil {
|
||||||
|
return nil, fmt.Errorf("%s Config is not available, connectionState=%s",
|
||||||
|
v.Reference(), o.Summary.Runtime.ConnectionState)
|
||||||
|
}
|
||||||
|
|
||||||
|
return VirtualDeviceList(o.Config.Hardware.Device), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) HostSystem(ctx context.Context) (*HostSystem, error) {
|
||||||
|
var o mo.VirtualMachine
|
||||||
|
|
||||||
|
err := v.Properties(ctx, v.Reference(), []string{"summary.runtime.host"}, &o)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
host := o.Summary.Runtime.Host
|
||||||
|
if host == nil {
|
||||||
|
return nil, errors.New("VM doesn't have a HostSystem")
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHostSystem(v.c, *host), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) ResourcePool(ctx context.Context) (*ResourcePool, error) {
|
||||||
|
var o mo.VirtualMachine
|
||||||
|
|
||||||
|
err := v.Properties(ctx, v.Reference(), []string{"resourcePool"}, &o)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rp := o.ResourcePool
|
||||||
|
if rp == nil {
|
||||||
|
return nil, errors.New("VM doesn't have a resourcePool")
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewResourcePool(v.c, *rp), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) configureDevice(ctx context.Context, op types.VirtualDeviceConfigSpecOperation, fop types.VirtualDeviceConfigSpecFileOperation, devices ...types.BaseVirtualDevice) error {
|
||||||
|
spec := types.VirtualMachineConfigSpec{}
|
||||||
|
|
||||||
|
for _, device := range devices {
|
||||||
|
config := &types.VirtualDeviceConfigSpec{
|
||||||
|
Device: device,
|
||||||
|
Operation: op,
|
||||||
|
}
|
||||||
|
|
||||||
|
if disk, ok := device.(*types.VirtualDisk); ok {
|
||||||
|
config.FileOperation = fop
|
||||||
|
|
||||||
|
// Special case to attach an existing disk
|
||||||
|
if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 {
|
||||||
|
childDisk := false
|
||||||
|
if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
|
||||||
|
childDisk = b.Parent != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !childDisk {
|
||||||
|
config.FileOperation = "" // existing disk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spec.DeviceChange = append(spec.DeviceChange, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
task, err := v.Reconfigure(ctx, spec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.Wait(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDevice adds the given devices to the VirtualMachine
|
||||||
|
func (v VirtualMachine) AddDevice(ctx context.Context, device ...types.BaseVirtualDevice) error {
|
||||||
|
return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationAdd, types.VirtualDeviceConfigSpecFileOperationCreate, device...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditDevice edits the given (existing) devices on the VirtualMachine
|
||||||
|
func (v VirtualMachine) EditDevice(ctx context.Context, device ...types.BaseVirtualDevice) error {
|
||||||
|
return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationEdit, types.VirtualDeviceConfigSpecFileOperationReplace, device...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveDevice removes the given devices on the VirtualMachine
|
||||||
|
func (v VirtualMachine) RemoveDevice(ctx context.Context, keepFiles bool, device ...types.BaseVirtualDevice) error {
|
||||||
|
fop := types.VirtualDeviceConfigSpecFileOperationDestroy
|
||||||
|
if keepFiles {
|
||||||
|
fop = ""
|
||||||
|
}
|
||||||
|
return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationRemove, fop, device...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BootOptions returns the VirtualMachine's config.bootOptions property.
|
||||||
|
func (v VirtualMachine) BootOptions(ctx context.Context) (*types.VirtualMachineBootOptions, error) {
|
||||||
|
var o mo.VirtualMachine
|
||||||
|
|
||||||
|
err := v.Properties(ctx, v.Reference(), []string{"config.bootOptions"}, &o)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return o.Config.BootOptions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBootOptions reconfigures the VirtualMachine with the given options.
|
||||||
|
func (v VirtualMachine) SetBootOptions(ctx context.Context, options *types.VirtualMachineBootOptions) error {
|
||||||
|
spec := types.VirtualMachineConfigSpec{}
|
||||||
|
|
||||||
|
spec.BootOptions = options
|
||||||
|
|
||||||
|
task, err := v.Reconfigure(ctx, spec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.Wait(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Answer answers a pending question.
|
||||||
|
func (v VirtualMachine) Answer(ctx context.Context, id, answer string) error {
|
||||||
|
req := types.AnswerVM{
|
||||||
|
This: v.Reference(),
|
||||||
|
QuestionId: id,
|
||||||
|
AnswerChoice: answer,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.AnswerVM(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSnapshot creates a new snapshot of a virtual machine.
|
||||||
|
func (v VirtualMachine) CreateSnapshot(ctx context.Context, name string, description string, memory bool, quiesce bool) (*Task, error) {
|
||||||
|
req := types.CreateSnapshot_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
Name: name,
|
||||||
|
Description: description,
|
||||||
|
Memory: memory,
|
||||||
|
Quiesce: quiesce,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreateSnapshot_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAllSnapshot removes all snapshots of a virtual machine
|
||||||
|
func (v VirtualMachine) RemoveAllSnapshot(ctx context.Context, consolidate *bool) (*Task, error) {
|
||||||
|
req := types.RemoveAllSnapshots_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
Consolidate: consolidate,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.RemoveAllSnapshots_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type snapshotMap map[string][]Reference
|
||||||
|
|
||||||
|
func (m snapshotMap) add(parent string, tree []types.VirtualMachineSnapshotTree) {
|
||||||
|
for i, st := range tree {
|
||||||
|
sname := st.Name
|
||||||
|
names := []string{sname, st.Snapshot.Value}
|
||||||
|
|
||||||
|
if parent != "" {
|
||||||
|
sname = path.Join(parent, sname)
|
||||||
|
// Add full path as an option to resolve duplicate names
|
||||||
|
names = append(names, sname)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
m[name] = append(m[name], &tree[i].Snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.add(sname, st.ChildSnapshotList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindSnapshot supports snapshot lookup by name, where name can be:
|
||||||
|
// 1) snapshot ManagedObjectReference.Value (unique)
|
||||||
|
// 2) snapshot name (may not be unique)
|
||||||
|
// 3) snapshot tree path (may not be unique)
|
||||||
|
func (v VirtualMachine) FindSnapshot(ctx context.Context, name string) (Reference, error) {
|
||||||
|
var o mo.VirtualMachine
|
||||||
|
|
||||||
|
err := v.Properties(ctx, v.Reference(), []string{"snapshot"}, &o)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.Snapshot == nil || len(o.Snapshot.RootSnapshotList) == 0 {
|
||||||
|
return nil, errors.New("No snapshots for this VM")
|
||||||
|
}
|
||||||
|
|
||||||
|
m := make(snapshotMap)
|
||||||
|
m.add("", o.Snapshot.RootSnapshotList)
|
||||||
|
|
||||||
|
s := m[name]
|
||||||
|
switch len(s) {
|
||||||
|
case 0:
|
||||||
|
return nil, fmt.Errorf("snapshot %q not found", name)
|
||||||
|
case 1:
|
||||||
|
return s[0], nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("%q resolves to %d snapshots", name, len(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveSnapshot removes a named snapshot
|
||||||
|
func (v VirtualMachine) RemoveSnapshot(ctx context.Context, name string, removeChildren bool, consolidate *bool) (*Task, error) {
|
||||||
|
snapshot, err := v.FindSnapshot(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.RemoveSnapshot_Task{
|
||||||
|
This: snapshot.Reference(),
|
||||||
|
RemoveChildren: removeChildren,
|
||||||
|
Consolidate: consolidate,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.RemoveSnapshot_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevertToCurrentSnapshot reverts to the current snapshot
|
||||||
|
func (v VirtualMachine) RevertToCurrentSnapshot(ctx context.Context, suppressPowerOn bool) (*Task, error) {
|
||||||
|
req := types.RevertToCurrentSnapshot_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
SuppressPowerOn: types.NewBool(suppressPowerOn),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.RevertToCurrentSnapshot_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevertToSnapshot reverts to a named snapshot
|
||||||
|
func (v VirtualMachine) RevertToSnapshot(ctx context.Context, name string, suppressPowerOn bool) (*Task, error) {
|
||||||
|
snapshot, err := v.FindSnapshot(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.RevertToSnapshot_Task{
|
||||||
|
This: snapshot.Reference(),
|
||||||
|
SuppressPowerOn: types.NewBool(suppressPowerOn),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.RevertToSnapshot_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsToolsRunning returns true if VMware Tools is currently running in the guest OS, and false otherwise.
|
||||||
|
func (v VirtualMachine) IsToolsRunning(ctx context.Context) (bool, error) {
|
||||||
|
var o mo.VirtualMachine
|
||||||
|
|
||||||
|
err := v.Properties(ctx, v.Reference(), []string{"guest.toolsRunningStatus"}, &o)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return o.Guest.ToolsRunningStatus == string(types.VirtualMachineToolsRunningStatusGuestToolsRunning), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the VirtualMachine to change to the desired power state.
|
||||||
|
func (v VirtualMachine) WaitForPowerState(ctx context.Context, state types.VirtualMachinePowerState) error {
|
||||||
|
p := property.DefaultCollector(v.c)
|
||||||
|
err := property.Wait(ctx, p, v.Reference(), []string{PropRuntimePowerState}, func(pc []types.PropertyChange) bool {
|
||||||
|
for _, c := range pc {
|
||||||
|
if c.Name != PropRuntimePowerState {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c.Val == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ps := c.Val.(types.VirtualMachinePowerState)
|
||||||
|
if ps == state {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) MarkAsTemplate(ctx context.Context) error {
|
||||||
|
req := types.MarkAsTemplate{
|
||||||
|
This: v.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.MarkAsTemplate(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) MarkAsVirtualMachine(ctx context.Context, pool ResourcePool, host *HostSystem) error {
|
||||||
|
req := types.MarkAsVirtualMachine{
|
||||||
|
This: v.Reference(),
|
||||||
|
Pool: pool.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if host != nil {
|
||||||
|
ref := host.Reference()
|
||||||
|
req.Host = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.MarkAsVirtualMachine(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) Migrate(ctx context.Context, pool *ResourcePool, host *HostSystem, priority types.VirtualMachineMovePriority, state types.VirtualMachinePowerState) (*Task, error) {
|
||||||
|
req := types.MigrateVM_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
Priority: priority,
|
||||||
|
State: state,
|
||||||
|
}
|
||||||
|
|
||||||
|
if pool != nil {
|
||||||
|
ref := pool.Reference()
|
||||||
|
req.Pool = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
if host != nil {
|
||||||
|
ref := host.Reference()
|
||||||
|
req.Host = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.MigrateVM_Task(ctx, v.c, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) Unregister(ctx context.Context) error {
|
||||||
|
req := types.UnregisterVM{
|
||||||
|
This: v.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UnregisterVM(ctx, v.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryEnvironmentBrowser is a helper to get the environmentBrowser property.
|
||||||
|
func (v VirtualMachine) QueryConfigTarget(ctx context.Context) (*types.ConfigTarget, error) {
|
||||||
|
var vm mo.VirtualMachine
|
||||||
|
|
||||||
|
err := v.Properties(ctx, v.Reference(), []string{"environmentBrowser"}, &vm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.QueryConfigTarget{
|
||||||
|
This: vm.EnvironmentBrowser,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.QueryConfigTarget(ctx, v.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) MountToolsInstaller(ctx context.Context) error {
|
||||||
|
req := types.MountToolsInstaller{
|
||||||
|
This: v.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.MountToolsInstaller(ctx, v.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) UnmountToolsInstaller(ctx context.Context) error {
|
||||||
|
req := types.UnmountToolsInstaller{
|
||||||
|
This: v.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.UnmountToolsInstaller(ctx, v.Client(), &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VirtualMachine) UpgradeTools(ctx context.Context, options string) (*Task, error) {
|
||||||
|
req := types.UpgradeTools_Task{
|
||||||
|
This: v.Reference(),
|
||||||
|
InstallerOptions: options,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.UpgradeTools_Task(ctx, v.Client(), &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTask(v.c, res.Returnval), nil
|
||||||
|
}
|
21
vendor/github.com/vmware/govmomi/object/vmware_distributed_virtual_switch.go
generated
vendored
Normal file
21
vendor/github.com/vmware/govmomi/object/vmware_distributed_virtual_switch.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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
|
||||||
|
|
||||||
|
type VmwareDistributedVirtualSwitch struct {
|
||||||
|
DistributedVirtualSwitch
|
||||||
|
}
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 property
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Collector models the PropertyCollector managed object.
|
||||||
|
//
|
||||||
|
// For more information, see:
|
||||||
|
// http://pubs.vmware.com/vsphere-60/index.jsp?topic=%2Fcom.vmware.wssdk.apiref.doc%2Fvmodl.query.PropertyCollector.html
|
||||||
|
//
|
||||||
|
type Collector struct {
|
||||||
|
roundTripper soap.RoundTripper
|
||||||
|
reference types.ManagedObjectReference
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultCollector returns the session's default property collector.
|
||||||
|
func DefaultCollector(c *vim25.Client) *Collector {
|
||||||
|
p := Collector{
|
||||||
|
roundTripper: c,
|
||||||
|
reference: c.ServiceContent.PropertyCollector,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Collector) Reference() types.ManagedObjectReference {
|
||||||
|
return p.reference
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates a new session-specific Collector that can be used to
|
||||||
|
// retrieve property updates independent of any other Collector.
|
||||||
|
func (p *Collector) Create(ctx context.Context) (*Collector, error) {
|
||||||
|
req := types.CreatePropertyCollector{
|
||||||
|
This: p.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.CreatePropertyCollector(ctx, p.roundTripper, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newp := Collector{
|
||||||
|
roundTripper: p.roundTripper,
|
||||||
|
reference: res.Returnval,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &newp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy destroys this Collector.
|
||||||
|
func (p *Collector) Destroy(ctx context.Context) error {
|
||||||
|
req := types.DestroyPropertyCollector{
|
||||||
|
This: p.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.DestroyPropertyCollector(ctx, p.roundTripper, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.reference = types.ManagedObjectReference{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Collector) CreateFilter(ctx context.Context, req types.CreateFilter) error {
|
||||||
|
req.This = p.Reference()
|
||||||
|
|
||||||
|
_, err := methods.CreateFilter(ctx, p.roundTripper, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Collector) WaitForUpdates(ctx context.Context, v string) (*types.UpdateSet, error) {
|
||||||
|
req := types.WaitForUpdatesEx{
|
||||||
|
This: p.Reference(),
|
||||||
|
Version: v,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.WaitForUpdatesEx(ctx, p.roundTripper, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Collector) RetrieveProperties(ctx context.Context, req types.RetrieveProperties) (*types.RetrievePropertiesResponse, error) {
|
||||||
|
req.This = p.Reference()
|
||||||
|
return methods.RetrieveProperties(ctx, p.roundTripper, &req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve loads properties for a slice of managed objects. The dst argument
|
||||||
|
// must be a pointer to a []interface{}, which is populated with the instances
|
||||||
|
// of the specified managed objects, with the relevant properties filled in. If
|
||||||
|
// the properties slice is nil, all properties are loaded.
|
||||||
|
func (p *Collector) Retrieve(ctx context.Context, objs []types.ManagedObjectReference, ps []string, dst interface{}) error {
|
||||||
|
var propSpec *types.PropertySpec
|
||||||
|
var objectSet []types.ObjectSpec
|
||||||
|
|
||||||
|
for _, obj := range objs {
|
||||||
|
// Ensure that all object reference types are the same
|
||||||
|
if propSpec == nil {
|
||||||
|
propSpec = &types.PropertySpec{
|
||||||
|
Type: obj.Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
if ps == nil {
|
||||||
|
propSpec.All = types.NewBool(true)
|
||||||
|
} else {
|
||||||
|
propSpec.PathSet = ps
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if obj.Type != propSpec.Type {
|
||||||
|
return errors.New("object references must have the same type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
objectSpec := types.ObjectSpec{
|
||||||
|
Obj: obj,
|
||||||
|
Skip: types.NewBool(false),
|
||||||
|
}
|
||||||
|
|
||||||
|
objectSet = append(objectSet, objectSpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.RetrieveProperties{
|
||||||
|
SpecSet: []types.PropertyFilterSpec{
|
||||||
|
{
|
||||||
|
ObjectSet: objectSet,
|
||||||
|
PropSet: []types.PropertySpec{*propSpec},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := p.RetrieveProperties(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if d, ok := dst.(*[]types.ObjectContent); ok {
|
||||||
|
*d = res.Returnval
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return mo.LoadRetrievePropertiesResponse(res, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveWithFilter populates dst as Retrieve does, but only for entities matching the given filter.
|
||||||
|
func (p *Collector) RetrieveWithFilter(ctx context.Context, objs []types.ManagedObjectReference, ps []string, dst interface{}, filter Filter) error {
|
||||||
|
if len(filter) == 0 {
|
||||||
|
return p.Retrieve(ctx, objs, ps, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
var content []types.ObjectContent
|
||||||
|
|
||||||
|
err := p.Retrieve(ctx, objs, filter.Keys(), &content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
objs = filter.MatchObjectContent(content)
|
||||||
|
|
||||||
|
if len(objs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.Retrieve(ctx, objs, ps, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveOne calls Retrieve with a single managed object reference.
|
||||||
|
func (p *Collector) RetrieveOne(ctx context.Context, obj types.ManagedObjectReference, ps []string, dst interface{}) error {
|
||||||
|
var objs = []types.ManagedObjectReference{obj}
|
||||||
|
return p.Retrieve(ctx, objs, ps, dst)
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 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 property
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filter provides methods for matching against types.DynamicProperty
|
||||||
|
type Filter map[string]types.AnyType
|
||||||
|
|
||||||
|
// Keys returns the Filter map keys as a []string
|
||||||
|
func (f Filter) Keys() []string {
|
||||||
|
keys := make([]string, 0, len(f))
|
||||||
|
|
||||||
|
for key := range f {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchProperty returns true if a Filter entry matches the given prop.
|
||||||
|
func (f Filter) MatchProperty(prop types.DynamicProperty) bool {
|
||||||
|
match, ok := f[prop.Name]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if match == prop.Val {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
ptype := reflect.TypeOf(prop.Val)
|
||||||
|
|
||||||
|
if strings.HasPrefix(ptype.Name(), "ArrayOf") {
|
||||||
|
pval := reflect.ValueOf(prop.Val).Field(0)
|
||||||
|
|
||||||
|
for i := 0; i < pval.Len(); i++ {
|
||||||
|
prop.Val = pval.Index(i).Interface()
|
||||||
|
|
||||||
|
if f.MatchProperty(prop) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.TypeOf(match) != ptype {
|
||||||
|
s, ok := match.(string)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert if we can
|
||||||
|
switch prop.Val.(type) {
|
||||||
|
case bool:
|
||||||
|
match, _ = strconv.ParseBool(s)
|
||||||
|
case int16:
|
||||||
|
x, _ := strconv.ParseInt(s, 10, 16)
|
||||||
|
match = int16(x)
|
||||||
|
case int32:
|
||||||
|
x, _ := strconv.ParseInt(s, 10, 32)
|
||||||
|
match = int32(x)
|
||||||
|
case int64:
|
||||||
|
match, _ = strconv.ParseInt(s, 10, 64)
|
||||||
|
case float32:
|
||||||
|
x, _ := strconv.ParseFloat(s, 32)
|
||||||
|
match = float32(x)
|
||||||
|
case float64:
|
||||||
|
match, _ = strconv.ParseFloat(s, 64)
|
||||||
|
case fmt.Stringer:
|
||||||
|
prop.Val = prop.Val.(fmt.Stringer).String()
|
||||||
|
default:
|
||||||
|
if ptype.Kind() != reflect.String {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// An enum type we can convert to a string type
|
||||||
|
prop.Val = reflect.ValueOf(prop.Val).String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pval := prop.Val.(type) {
|
||||||
|
case string:
|
||||||
|
s := match.(string)
|
||||||
|
if s == "*" {
|
||||||
|
return true // TODO: path.Match fails if s contains a '/'
|
||||||
|
}
|
||||||
|
m, _ := path.Match(s, pval)
|
||||||
|
return m
|
||||||
|
default:
|
||||||
|
return reflect.DeepEqual(match, pval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchPropertyList returns true if all given props match the Filter.
|
||||||
|
func (f Filter) MatchPropertyList(props []types.DynamicProperty) bool {
|
||||||
|
for _, p := range props {
|
||||||
|
if !f.MatchProperty(p) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchObjectContent returns a list of ObjectContent.Obj where the ObjectContent.PropSet matches the Filter.
|
||||||
|
func (f Filter) MatchObjectContent(objects []types.ObjectContent) []types.ManagedObjectReference {
|
||||||
|
var refs []types.ManagedObjectReference
|
||||||
|
|
||||||
|
for _, o := range objects {
|
||||||
|
if f.MatchPropertyList(o.PropSet) {
|
||||||
|
refs = append(refs, o.Obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return refs
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 property
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Wait waits for any of the specified properties of the specified managed
|
||||||
|
// object to change. It calls the specified function for every update it
|
||||||
|
// receives. If this function returns false, it continues waiting for
|
||||||
|
// subsequent updates. If this function returns true, it stops waiting and
|
||||||
|
// returns.
|
||||||
|
//
|
||||||
|
// To only receive updates for the specified managed object, the function
|
||||||
|
// creates a new property collector and calls CreateFilter. A new property
|
||||||
|
// collector is required because filters can only be added, not removed.
|
||||||
|
//
|
||||||
|
// The newly created collector is destroyed before this function returns (both
|
||||||
|
// in case of success or error).
|
||||||
|
//
|
||||||
|
func Wait(ctx context.Context, c *Collector, obj types.ManagedObjectReference, ps []string, f func([]types.PropertyChange) bool) error {
|
||||||
|
p, err := c.Create(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to destroy the collector using the background context, as the
|
||||||
|
// specified context may have timed out or have been cancelled.
|
||||||
|
defer p.Destroy(context.Background())
|
||||||
|
|
||||||
|
req := types.CreateFilter{
|
||||||
|
Spec: types.PropertyFilterSpec{
|
||||||
|
ObjectSet: []types.ObjectSpec{
|
||||||
|
{
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PropSet: []types.PropertySpec{
|
||||||
|
{
|
||||||
|
PathSet: ps,
|
||||||
|
Type: obj.Type,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ps) == 0 {
|
||||||
|
req.Spec.PropSet[0].All = types.NewBool(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.CreateFilter(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return waitLoop(ctx, p, func(_ types.ManagedObjectReference, pc []types.PropertyChange) bool {
|
||||||
|
return f(pc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForView waits for any of the specified properties of the managed
|
||||||
|
// objects in the View to change. It calls the specified function for every update it
|
||||||
|
// receives. If this function returns false, it continues waiting for
|
||||||
|
// subsequent updates. If this function returns true, it stops waiting and
|
||||||
|
// returns.
|
||||||
|
//
|
||||||
|
// To only receive updates for the View's specified managed objects, the function
|
||||||
|
// creates a new property collector and calls CreateFilter. A new property
|
||||||
|
// collector is required because filters can only be added, not removed.
|
||||||
|
//
|
||||||
|
// The newly created collector is destroyed before this function returns (both
|
||||||
|
// in case of success or error).
|
||||||
|
//
|
||||||
|
// The code assumes that all objects in the View are the same type
|
||||||
|
func WaitForView(ctx context.Context, c *Collector, view types.ManagedObjectReference, obj types.ManagedObjectReference, ps []string, f func(types.ManagedObjectReference, []types.PropertyChange) bool) error {
|
||||||
|
p, err := c.Create(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to destroy the collector using the background context, as the
|
||||||
|
// specified context may have timed out or have been cancelled.
|
||||||
|
defer p.Destroy(context.Background())
|
||||||
|
|
||||||
|
req := types.CreateFilter{
|
||||||
|
Spec: types.PropertyFilterSpec{
|
||||||
|
ObjectSet: []types.ObjectSpec{
|
||||||
|
{
|
||||||
|
Obj: view,
|
||||||
|
SelectSet: []types.BaseSelectionSpec{
|
||||||
|
&types.TraversalSpec{
|
||||||
|
SelectionSpec: types.SelectionSpec{
|
||||||
|
Name: "traverseEntities",
|
||||||
|
},
|
||||||
|
Path: "view",
|
||||||
|
Type: view.Type}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PropSet: []types.PropertySpec{
|
||||||
|
{
|
||||||
|
Type: obj.Type,
|
||||||
|
PathSet: ps,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
|
||||||
|
err = p.CreateFilter(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return waitLoop(ctx, p, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitLoop(ctx context.Context, c *Collector, f func(types.ManagedObjectReference, []types.PropertyChange) bool) error {
|
||||||
|
for version := ""; ; {
|
||||||
|
res, err := c.WaitForUpdates(ctx, version)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry if the result came back empty
|
||||||
|
if res == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
version = res.Version
|
||||||
|
|
||||||
|
for _, fs := range res.FilterSet {
|
||||||
|
for _, os := range fs.ObjectSet {
|
||||||
|
if f(os.Obj, os.ChangeSet) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 session
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type keepAlive struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
roundTripper soap.RoundTripper
|
||||||
|
idleTime time.Duration
|
||||||
|
notifyRequest chan struct{}
|
||||||
|
notifyStop chan struct{}
|
||||||
|
notifyWaitGroup sync.WaitGroup
|
||||||
|
|
||||||
|
// keepAlive executes a request in the background with the purpose of
|
||||||
|
// keeping the session active. The response for this request is discarded.
|
||||||
|
keepAlive func(soap.RoundTripper) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultKeepAlive(roundTripper soap.RoundTripper) error {
|
||||||
|
_, _ = methods.GetCurrentTime(context.Background(), roundTripper)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeepAlive wraps the specified soap.RoundTripper and executes a meaningless
|
||||||
|
// API request in the background after the RoundTripper has been idle for the
|
||||||
|
// specified amount of idle time. The keep alive process only starts once a
|
||||||
|
// user logs in and runs until the user logs out again.
|
||||||
|
func KeepAlive(roundTripper soap.RoundTripper, idleTime time.Duration) soap.RoundTripper {
|
||||||
|
return KeepAliveHandler(roundTripper, idleTime, defaultKeepAlive)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeepAliveHandler works as KeepAlive() does, but the handler param can decide how to handle errors.
|
||||||
|
// For example, if connectivity to ESX/VC is down long enough for a session to expire, a handler can choose to
|
||||||
|
// Login() on a types.NotAuthenticated error. If handler returns non-nil, the keep alive go routine will be stopped.
|
||||||
|
func KeepAliveHandler(roundTripper soap.RoundTripper, idleTime time.Duration, handler func(soap.RoundTripper) error) soap.RoundTripper {
|
||||||
|
k := &keepAlive{
|
||||||
|
roundTripper: roundTripper,
|
||||||
|
idleTime: idleTime,
|
||||||
|
notifyRequest: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
k.keepAlive = handler
|
||||||
|
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *keepAlive) start() {
|
||||||
|
k.Lock()
|
||||||
|
defer k.Unlock()
|
||||||
|
|
||||||
|
if k.notifyStop != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// This channel must be closed to terminate idle timer.
|
||||||
|
k.notifyStop = make(chan struct{})
|
||||||
|
k.notifyWaitGroup.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer k.notifyWaitGroup.Done()
|
||||||
|
|
||||||
|
for t := time.NewTimer(k.idleTime); ; {
|
||||||
|
select {
|
||||||
|
case <-k.notifyStop:
|
||||||
|
return
|
||||||
|
case <-k.notifyRequest:
|
||||||
|
t.Reset(k.idleTime)
|
||||||
|
case <-t.C:
|
||||||
|
if err := k.keepAlive(k.roundTripper); err != nil {
|
||||||
|
k.stop()
|
||||||
|
}
|
||||||
|
t = time.NewTimer(k.idleTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *keepAlive) stop() {
|
||||||
|
k.Lock()
|
||||||
|
defer k.Unlock()
|
||||||
|
|
||||||
|
if k.notifyStop != nil {
|
||||||
|
close(k.notifyStop)
|
||||||
|
k.notifyWaitGroup.Wait()
|
||||||
|
k.notifyStop = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *keepAlive) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
|
||||||
|
err := k.roundTripper.RoundTrip(ctx, req, res)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start ticker on login, stop ticker on logout.
|
||||||
|
switch req.(type) {
|
||||||
|
case *methods.LoginBody, *methods.LoginExtensionByCertificateBody:
|
||||||
|
k.start()
|
||||||
|
case *methods.LogoutBody:
|
||||||
|
k.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 session
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/property"
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Locale defaults to "en_US" and can be overridden via this var or the GOVMOMI_LOCALE env var.
|
||||||
|
// A value of "_" uses the server locale setting.
|
||||||
|
var Locale = os.Getenv("GOVMOMI_LOCALE")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if Locale == "_" {
|
||||||
|
Locale = ""
|
||||||
|
} else if Locale == "" {
|
||||||
|
Locale = "en_US"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Manager struct {
|
||||||
|
client *vim25.Client
|
||||||
|
userSession *types.UserSession
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManager(client *vim25.Client) *Manager {
|
||||||
|
m := Manager{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm Manager) Reference() types.ManagedObjectReference {
|
||||||
|
return *sm.client.ServiceContent.SessionManager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *Manager) SetLocale(ctx context.Context, locale string) error {
|
||||||
|
req := types.SetLocale{
|
||||||
|
This: sm.Reference(),
|
||||||
|
Locale: locale,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.SetLocale(ctx, sm.client, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *Manager) Login(ctx context.Context, u *url.Userinfo) error {
|
||||||
|
req := types.Login{
|
||||||
|
This: sm.Reference(),
|
||||||
|
Locale: Locale,
|
||||||
|
}
|
||||||
|
|
||||||
|
if u != nil {
|
||||||
|
req.UserName = u.Username()
|
||||||
|
if pw, ok := u.Password(); ok {
|
||||||
|
req.Password = pw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
login, err := methods.Login(ctx, sm.client, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sm.userSession = &login.Returnval
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *Manager) LoginExtensionByCertificate(ctx context.Context, key string, locale string) error {
|
||||||
|
req := types.LoginExtensionByCertificate{
|
||||||
|
This: sm.Reference(),
|
||||||
|
ExtensionKey: key,
|
||||||
|
Locale: locale,
|
||||||
|
}
|
||||||
|
|
||||||
|
login, err := methods.LoginExtensionByCertificate(ctx, sm.client, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sm.userSession = &login.Returnval
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *Manager) Logout(ctx context.Context) error {
|
||||||
|
req := types.Logout{
|
||||||
|
This: sm.Reference(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.Logout(ctx, sm.client, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sm.userSession = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserSession retrieves and returns the SessionManager's CurrentSession field.
|
||||||
|
// Nil is returned if the session is not authenticated.
|
||||||
|
func (sm *Manager) UserSession(ctx context.Context) (*types.UserSession, error) {
|
||||||
|
var mgr mo.SessionManager
|
||||||
|
|
||||||
|
pc := property.DefaultCollector(sm.client)
|
||||||
|
err := pc.RetrieveOne(ctx, sm.Reference(), []string{"currentSession"}, &mgr)
|
||||||
|
if err != nil {
|
||||||
|
// It's OK if we can't retrieve properties because we're not authenticated
|
||||||
|
if f, ok := err.(types.HasFault); ok {
|
||||||
|
switch f.Fault().(type) {
|
||||||
|
case *types.NotAuthenticated:
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return mgr.CurrentSession, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *Manager) TerminateSession(ctx context.Context, sessionId []string) error {
|
||||||
|
req := types.TerminateSession{
|
||||||
|
This: sm.Reference(),
|
||||||
|
SessionId: sessionId,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := methods.TerminateSession(ctx, sm.client, &req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionIsActive checks whether the session that was created at login is
|
||||||
|
// still valid. This function only works against vCenter.
|
||||||
|
func (sm *Manager) SessionIsActive(ctx context.Context) (bool, error) {
|
||||||
|
if sm.userSession == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.SessionIsActive{
|
||||||
|
This: sm.Reference(),
|
||||||
|
SessionID: sm.userSession.Key,
|
||||||
|
UserName: sm.userSession.UserName,
|
||||||
|
}
|
||||||
|
|
||||||
|
active, err := methods.SessionIsActive(ctx, sm.client, &req)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return active.Returnval, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *Manager) AcquireGenericServiceTicket(ctx context.Context, spec types.BaseSessionManagerServiceRequestSpec) (*types.SessionManagerGenericServiceTicket, error) {
|
||||||
|
req := types.AcquireGenericServiceTicket{
|
||||||
|
This: sm.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.AcquireGenericServiceTicket(ctx, sm.client, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *Manager) AcquireLocalTicket(ctx context.Context, userName string) (*types.SessionManagerLocalTicket, error) {
|
||||||
|
req := types.AcquireLocalTicket{
|
||||||
|
This: sm.Reference(),
|
||||||
|
UserName: userName,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := methods.AcquireLocalTicket(ctx, sm.client, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 task
|
||||||
|
|
||||||
|
import "github.com/vmware/govmomi/vim25/types"
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
*types.LocalizedMethodFault
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the task's localized fault message.
|
||||||
|
func (e Error) Error() string {
|
||||||
|
return e.LocalizedMethodFault.LocalizedMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Error) Fault() types.BaseMethodFault {
|
||||||
|
return e.LocalizedMethodFault.Fault
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/property"
|
||||||
|
"github.com/vmware/govmomi/vim25/progress"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type taskProgress struct {
|
||||||
|
info *types.TaskInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t taskProgress) Percentage() float32 {
|
||||||
|
return float32(t.info.Progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t taskProgress) Detail() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t taskProgress) Error() error {
|
||||||
|
if t.info.Error != nil {
|
||||||
|
return Error{t.info.Error}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type taskCallback struct {
|
||||||
|
ch chan<- progress.Report
|
||||||
|
info *types.TaskInfo
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *taskCallback) fn(pc []types.PropertyChange) bool {
|
||||||
|
for _, c := range pc {
|
||||||
|
if c.Name != "info" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Op != types.PropertyChangeOpAssign {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Val == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ti := c.Val.(types.TaskInfo)
|
||||||
|
t.info = &ti
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.info could be nil if pc can't satify the rules above
|
||||||
|
if t.info == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
pr := taskProgress{t.info}
|
||||||
|
|
||||||
|
// Store copy of error, so Wait() can return it as well.
|
||||||
|
t.err = pr.Error()
|
||||||
|
|
||||||
|
switch t.info.State {
|
||||||
|
case types.TaskInfoStateQueued, types.TaskInfoStateRunning:
|
||||||
|
if t.ch != nil {
|
||||||
|
// Don't care if this is dropped
|
||||||
|
select {
|
||||||
|
case t.ch <- pr:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
case types.TaskInfoStateSuccess, types.TaskInfoStateError:
|
||||||
|
if t.ch != nil {
|
||||||
|
// Last one must always be delivered
|
||||||
|
t.ch <- pr
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
panic("unknown state: " + t.info.State)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits for a task to finish with either success or failure. It does so
|
||||||
|
// by waiting for the "info" property of task managed object to change. The
|
||||||
|
// function returns when it finds the task in the "success" or "error" state.
|
||||||
|
// In the former case, the return value is nil. In the latter case the return
|
||||||
|
// value is an instance of this package's Error struct.
|
||||||
|
//
|
||||||
|
// Any error returned while waiting for property changes causes the function to
|
||||||
|
// return immediately and propagate the error.
|
||||||
|
//
|
||||||
|
// If the progress.Sinker argument is specified, any progress updates for the
|
||||||
|
// task are sent here. The completion percentage is passed through directly.
|
||||||
|
// The detail for the progress update is set to an empty string. If the task
|
||||||
|
// finishes in the error state, the error instance is passed through as well.
|
||||||
|
// Note that this error is the same error that is returned by this function.
|
||||||
|
//
|
||||||
|
func Wait(ctx context.Context, ref types.ManagedObjectReference, pc *property.Collector, s progress.Sinker) (*types.TaskInfo, error) {
|
||||||
|
cb := &taskCallback{}
|
||||||
|
|
||||||
|
// Include progress sink if specified
|
||||||
|
if s != nil {
|
||||||
|
cb.ch = s.Sink()
|
||||||
|
defer close(cb.ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := property.Wait(ctx, pc, ref, []string{"info"}, cb.fn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cb.info, cb.err
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015-2016 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 vim25
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client is a tiny wrapper around the vim25/soap Client that stores session
|
||||||
|
// specific state (i.e. state that only needs to be retrieved once after the
|
||||||
|
// client has been created). This means the client can be reused after
|
||||||
|
// serialization without performing additional requests for initialization.
|
||||||
|
type Client struct {
|
||||||
|
*soap.Client
|
||||||
|
|
||||||
|
ServiceContent types.ServiceContent
|
||||||
|
|
||||||
|
// RoundTripper is a separate field such that the client's implementation of
|
||||||
|
// the RoundTripper interface can be wrapped by separate implementations for
|
||||||
|
// extra functionality (for example, reauthentication on session timeout).
|
||||||
|
RoundTripper soap.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient creates and returns a new client wirh the ServiceContent field
|
||||||
|
// filled in.
|
||||||
|
func NewClient(ctx context.Context, rt soap.RoundTripper) (*Client, error) {
|
||||||
|
serviceContent, err := methods.GetServiceContent(ctx, rt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := Client{
|
||||||
|
ServiceContent: serviceContent,
|
||||||
|
RoundTripper: rt,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set client if it happens to be a soap.Client
|
||||||
|
if sc, ok := rt.(*soap.Client); ok {
|
||||||
|
c.Client = sc
|
||||||
|
}
|
||||||
|
|
||||||
|
return &c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundTrip dispatches to the RoundTripper field.
|
||||||
|
func (c *Client) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
|
||||||
|
return c.RoundTripper.RoundTrip(ctx, req, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
type marshaledClient struct {
|
||||||
|
SoapClient *soap.Client
|
||||||
|
ServiceContent types.ServiceContent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) MarshalJSON() ([]byte, error) {
|
||||||
|
m := marshaledClient{
|
||||||
|
SoapClient: c.Client,
|
||||||
|
ServiceContent: c.ServiceContent,
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) UnmarshalJSON(b []byte) error {
|
||||||
|
var m marshaledClient
|
||||||
|
|
||||||
|
err := json.Unmarshal(b, &m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*c = Client{
|
||||||
|
Client: m.SoapClient,
|
||||||
|
ServiceContent: m.ServiceContent,
|
||||||
|
RoundTripper: m.SoapClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid returns whether or not the client is valid and ready for use.
|
||||||
|
// This should be called after unmarshalling the client.
|
||||||
|
func (c *Client) Valid() bool {
|
||||||
|
if c == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Client == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use arbitrary pointer field in the service content.
|
||||||
|
// Doesn't matter which one, as long as it is populated by default.
|
||||||
|
if c.ServiceContent.SessionManager == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsVC returns true if we are connected to a vCenter
|
||||||
|
func (c *Client) IsVC() bool {
|
||||||
|
return c.ServiceContent.About.ApiType == "VirtualCenter"
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 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 debug
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provider specified the interface types must implement to be used as a
|
||||||
|
// debugging sink. Having multiple such sink implementations allows it to be
|
||||||
|
// changed externally (for example when running tests).
|
||||||
|
type Provider interface {
|
||||||
|
NewFile(s string) io.WriteCloser
|
||||||
|
Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentProvider Provider = nil
|
||||||
|
|
||||||
|
func SetProvider(p Provider) {
|
||||||
|
if currentProvider != nil {
|
||||||
|
currentProvider.Flush()
|
||||||
|
}
|
||||||
|
currentProvider = p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled returns whether debugging is enabled or not.
|
||||||
|
func Enabled() bool {
|
||||||
|
return currentProvider != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFile dispatches to the current provider's NewFile function.
|
||||||
|
func NewFile(s string) io.WriteCloser {
|
||||||
|
return currentProvider.NewFile(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush dispatches to the current provider's Flush function.
|
||||||
|
func Flush() {
|
||||||
|
currentProvider.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileProvider implements a debugging provider that creates a real file for
|
||||||
|
// every call to NewFile. It maintains a list of all files that it creates,
|
||||||
|
// such that it can close them when its Flush function is called.
|
||||||
|
type FileProvider struct {
|
||||||
|
Path string
|
||||||
|
|
||||||
|
files []*os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fp *FileProvider) NewFile(p string) io.WriteCloser {
|
||||||
|
f, err := os.Create(path.Join(fp.Path, p))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fp.files = append(fp.files, f)
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fp *FileProvider) Flush() {
|
||||||
|
for _, f := range fp.files {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 vim25 provides a minimal client implementation to use with other
|
||||||
|
packages in the vim25 tree. The code in this package intentionally does not
|
||||||
|
take any dependendies outside the vim25 tree.
|
||||||
|
|
||||||
|
The client implementation in this package embeds the soap.Client structure.
|
||||||
|
Additionally, it stores the value of the session's ServiceContent object. This
|
||||||
|
object stores references to a variety of subsystems, such as the root property
|
||||||
|
collector, the session manager, and the search index. The client is fully
|
||||||
|
functional after serialization and deserialization, without the need for
|
||||||
|
additional requests for initialization.
|
||||||
|
*/
|
||||||
|
package vim25
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014-2015 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 methods
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RetrieveDynamicTypeManagerBody struct {
|
||||||
|
Req *types.RetrieveDynamicTypeManager `xml:"urn:vim25 RetrieveDynamicTypeManager"`
|
||||||
|
Res *types.RetrieveDynamicTypeManagerResponse `xml:"urn:vim25 RetrieveDynamicTypeManagerResponse"`
|
||||||
|
Fault_ *soap.Fault
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RetrieveDynamicTypeManagerBody) Fault() *soap.Fault { return b.Fault_ }
|
||||||
|
|
||||||
|
func RetrieveDynamicTypeManager(ctx context.Context, r soap.RoundTripper, req *types.RetrieveDynamicTypeManager) (*types.RetrieveDynamicTypeManagerResponse, error) {
|
||||||
|
var reqBody, resBody RetrieveDynamicTypeManagerBody
|
||||||
|
|
||||||
|
reqBody.Req = req
|
||||||
|
|
||||||
|
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resBody.Res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type RetrieveManagedMethodExecuterBody struct {
|
||||||
|
Req *types.RetrieveManagedMethodExecuter `xml:"urn:vim25 RetrieveManagedMethodExecuter"`
|
||||||
|
Res *types.RetrieveManagedMethodExecuterResponse `xml:"urn:vim25 RetrieveManagedMethodExecuterResponse"`
|
||||||
|
Fault_ *soap.Fault
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RetrieveManagedMethodExecuterBody) Fault() *soap.Fault { return b.Fault_ }
|
||||||
|
|
||||||
|
func RetrieveManagedMethodExecuter(ctx context.Context, r soap.RoundTripper, req *types.RetrieveManagedMethodExecuter) (*types.RetrieveManagedMethodExecuterResponse, error) {
|
||||||
|
var reqBody, resBody RetrieveManagedMethodExecuterBody
|
||||||
|
|
||||||
|
reqBody.Req = req
|
||||||
|
|
||||||
|
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resBody.Res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DynamicTypeMgrQueryMoInstancesBody struct {
|
||||||
|
Req *types.DynamicTypeMgrQueryMoInstances `xml:"urn:vim25 DynamicTypeMgrQueryMoInstances"`
|
||||||
|
Res *types.DynamicTypeMgrQueryMoInstancesResponse `xml:"urn:vim25 DynamicTypeMgrQueryMoInstancesResponse"`
|
||||||
|
Fault_ *soap.Fault
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *DynamicTypeMgrQueryMoInstancesBody) Fault() *soap.Fault { return b.Fault_ }
|
||||||
|
|
||||||
|
func DynamicTypeMgrQueryMoInstances(ctx context.Context, r soap.RoundTripper, req *types.DynamicTypeMgrQueryMoInstances) (*types.DynamicTypeMgrQueryMoInstancesResponse, error) {
|
||||||
|
var reqBody, resBody DynamicTypeMgrQueryMoInstancesBody
|
||||||
|
|
||||||
|
reqBody.Req = req
|
||||||
|
|
||||||
|
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resBody.Res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DynamicTypeMgrQueryTypeInfoBody struct {
|
||||||
|
Req *types.DynamicTypeMgrQueryTypeInfo `xml:"urn:vim25 DynamicTypeMgrQueryTypeInfo"`
|
||||||
|
Res *types.DynamicTypeMgrQueryTypeInfoResponse `xml:"urn:vim25 DynamicTypeMgrQueryTypeInfoResponse"`
|
||||||
|
Fault_ *soap.Fault
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *DynamicTypeMgrQueryTypeInfoBody) Fault() *soap.Fault { return b.Fault_ }
|
||||||
|
|
||||||
|
func DynamicTypeMgrQueryTypeInfo(ctx context.Context, r soap.RoundTripper, req *types.DynamicTypeMgrQueryTypeInfo) (*types.DynamicTypeMgrQueryTypeInfoResponse, error) {
|
||||||
|
var reqBody, resBody DynamicTypeMgrQueryTypeInfoBody
|
||||||
|
|
||||||
|
reqBody.Req = req
|
||||||
|
|
||||||
|
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resBody.Res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecuteSoapBody struct {
|
||||||
|
Req *types.ExecuteSoap `xml:"urn:vim25 ExecuteSoap"`
|
||||||
|
Res *types.ExecuteSoapResponse `xml:"urn:vim25 ExecuteSoapResponse"`
|
||||||
|
Fault_ *soap.Fault
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ExecuteSoapBody) Fault() *soap.Fault { return b.Fault_ }
|
||||||
|
|
||||||
|
func ExecuteSoap(ctx context.Context, r soap.RoundTripper, req *types.ExecuteSoap) (*types.ExecuteSoapResponse, error) {
|
||||||
|
var reqBody, resBody ExecuteSoapBody
|
||||||
|
|
||||||
|
reqBody.Req = req
|
||||||
|
|
||||||
|
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resBody.Res, nil
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 methods
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ServiceInstance = types.ManagedObjectReference{
|
||||||
|
Type: "ServiceInstance",
|
||||||
|
Value: "ServiceInstance",
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetServiceContent(ctx context.Context, r soap.RoundTripper) (types.ServiceContent, error) {
|
||||||
|
req := types.RetrieveServiceContent{
|
||||||
|
This: ServiceInstance,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := RetrieveServiceContent(ctx, r, &req)
|
||||||
|
if err != nil {
|
||||||
|
return types.ServiceContent{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Returnval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCurrentTime(ctx context.Context, r soap.RoundTripper) (*time.Time, error) {
|
||||||
|
req := types.CurrentTime{
|
||||||
|
This: ServiceInstance,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := CurrentTime(ctx, r, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Returnval, nil
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 mo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ancestors returns the entire ancestry tree of a specified managed object.
|
||||||
|
// The return value includes the root node and the specified object itself.
|
||||||
|
func Ancestors(ctx context.Context, rt soap.RoundTripper, pc, obj types.ManagedObjectReference) ([]ManagedEntity, error) {
|
||||||
|
ospec := types.ObjectSpec{
|
||||||
|
Obj: obj,
|
||||||
|
SelectSet: []types.BaseSelectionSpec{
|
||||||
|
&types.TraversalSpec{
|
||||||
|
SelectionSpec: types.SelectionSpec{Name: "traverseParent"},
|
||||||
|
Type: "ManagedEntity",
|
||||||
|
Path: "parent",
|
||||||
|
Skip: types.NewBool(false),
|
||||||
|
SelectSet: []types.BaseSelectionSpec{
|
||||||
|
&types.SelectionSpec{Name: "traverseParent"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&types.TraversalSpec{
|
||||||
|
SelectionSpec: types.SelectionSpec{},
|
||||||
|
Type: "VirtualMachine",
|
||||||
|
Path: "parentVApp",
|
||||||
|
Skip: types.NewBool(false),
|
||||||
|
SelectSet: []types.BaseSelectionSpec{
|
||||||
|
&types.SelectionSpec{Name: "traverseParent"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Skip: types.NewBool(false),
|
||||||
|
}
|
||||||
|
|
||||||
|
pspec := []types.PropertySpec{
|
||||||
|
{
|
||||||
|
Type: "ManagedEntity",
|
||||||
|
PathSet: []string{"name", "parent"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "VirtualMachine",
|
||||||
|
PathSet: []string{"parentVApp"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
req := types.RetrieveProperties{
|
||||||
|
This: pc,
|
||||||
|
SpecSet: []types.PropertyFilterSpec{
|
||||||
|
{
|
||||||
|
ObjectSet: []types.ObjectSpec{ospec},
|
||||||
|
PropSet: pspec,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var ifaces []interface{}
|
||||||
|
err := RetrievePropertiesForRequest(ctx, rt, req, &ifaces)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var out []ManagedEntity
|
||||||
|
|
||||||
|
// Build ancestry tree by iteratively finding a new child.
|
||||||
|
for len(out) < len(ifaces) {
|
||||||
|
var find types.ManagedObjectReference
|
||||||
|
|
||||||
|
if len(out) > 0 {
|
||||||
|
find = out[len(out)-1].Self
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find entity we're looking for given the last entity in the current tree.
|
||||||
|
for _, iface := range ifaces {
|
||||||
|
me := iface.(IsManagedEntity).GetManagedEntity()
|
||||||
|
|
||||||
|
if me.Name == "" {
|
||||||
|
// The types below have their own 'Name' field, so ManagedEntity.Name (me.Name) is empty.
|
||||||
|
// We only hit this case when the 'obj' param is one of these types.
|
||||||
|
// In most cases, 'obj' is a Folder so Name isn't collected in this call.
|
||||||
|
switch x := iface.(type) {
|
||||||
|
case Network:
|
||||||
|
me.Name = x.Name
|
||||||
|
case DistributedVirtualSwitch:
|
||||||
|
me.Name = x.Name
|
||||||
|
case DistributedVirtualPortgroup:
|
||||||
|
me.Name = x.Name
|
||||||
|
case OpaqueNetwork:
|
||||||
|
me.Name = x.Name
|
||||||
|
default:
|
||||||
|
// ManagedEntity always has a Name, if we hit this point we missed a case above.
|
||||||
|
panic(fmt.Sprintf("%#v Name is empty", me.Reference()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.Parent == nil {
|
||||||
|
// Special case for VirtualMachine within VirtualApp,
|
||||||
|
// unlikely to hit this other than via Finder.Element()
|
||||||
|
switch x := iface.(type) {
|
||||||
|
case VirtualMachine:
|
||||||
|
me.Parent = x.ParentVApp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.Parent == nil {
|
||||||
|
out = append(out, me)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if *me.Parent == find {
|
||||||
|
out = append(out, me)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 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 mo
|
||||||
|
|
||||||
|
// Entity is the interface that is implemented by all managed objects
|
||||||
|
// that extend ManagedEntity.
|
||||||
|
type Entity interface {
|
||||||
|
Reference
|
||||||
|
Entity() *ManagedEntity
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 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 mo
|
||||||
|
|
||||||
|
type IsManagedEntity interface {
|
||||||
|
GetManagedEntity() ManagedEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ComputeResource) GetManagedEntity() ManagedEntity {
|
||||||
|
return m.ManagedEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Datacenter) GetManagedEntity() ManagedEntity {
|
||||||
|
return m.ManagedEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Datastore) GetManagedEntity() ManagedEntity {
|
||||||
|
return m.ManagedEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m DistributedVirtualSwitch) GetManagedEntity() ManagedEntity {
|
||||||
|
return m.ManagedEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m DistributedVirtualPortgroup) GetManagedEntity() ManagedEntity {
|
||||||
|
return m.ManagedEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Folder) GetManagedEntity() ManagedEntity {
|
||||||
|
return m.ManagedEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HostSystem) GetManagedEntity() ManagedEntity {
|
||||||
|
return m.ManagedEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Network) GetManagedEntity() ManagedEntity {
|
||||||
|
return m.ManagedEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ResourcePool) GetManagedEntity() ManagedEntity {
|
||||||
|
return m.ManagedEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m VirtualMachine) GetManagedEntity() ManagedEntity {
|
||||||
|
return m.ManagedEntity
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 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 mo
|
||||||
|
|
||||||
|
import "github.com/vmware/govmomi/vim25/types"
|
||||||
|
|
||||||
|
// Reference is the interface that is implemented by all the managed objects
|
||||||
|
// defined in this package. It specifies that these managed objects have a
|
||||||
|
// function that returns the managed object reference to themselves.
|
||||||
|
type Reference interface {
|
||||||
|
Reference() types.ManagedObjectReference
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 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 mo
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
var t = map[string]reflect.Type{}
|
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014-2015 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 mo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ignoreMissingProperty(ref types.ManagedObjectReference, p types.MissingProperty) bool {
|
||||||
|
switch ref.Type {
|
||||||
|
case "VirtualMachine":
|
||||||
|
switch p.Path {
|
||||||
|
case "environmentBrowser":
|
||||||
|
// See https://github.com/vmware/govmomi/pull/242
|
||||||
|
return true
|
||||||
|
case "alarmActionsEnabled":
|
||||||
|
// Seen with vApp child VM
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObjectContentToType loads an ObjectContent value into the value it
|
||||||
|
// represents. If the ObjectContent value has a non-empty 'MissingSet' field,
|
||||||
|
// it returns the first fault it finds there as error. If the 'MissingSet'
|
||||||
|
// field is empty, it returns a pointer to a reflect.Value. It handles contain
|
||||||
|
// nested properties, such as 'guest.ipAddress' or 'config.hardware'.
|
||||||
|
func ObjectContentToType(o types.ObjectContent) (interface{}, error) {
|
||||||
|
// Expect no properties in the missing set
|
||||||
|
for _, p := range o.MissingSet {
|
||||||
|
if ignoreMissingProperty(o.Obj, p) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, soap.WrapVimFault(p.Fault.Fault)
|
||||||
|
}
|
||||||
|
|
||||||
|
ti := typeInfoForType(o.Obj.Type)
|
||||||
|
v, err := ti.LoadFromObjectContent(o)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Elem().Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadRetrievePropertiesResponse converts the response of a call to
|
||||||
|
// RetrieveProperties to one or more managed objects.
|
||||||
|
func LoadRetrievePropertiesResponse(res *types.RetrievePropertiesResponse, dst interface{}) error {
|
||||||
|
rt := reflect.TypeOf(dst)
|
||||||
|
if rt == nil || rt.Kind() != reflect.Ptr {
|
||||||
|
panic("need pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
rv := reflect.ValueOf(dst).Elem()
|
||||||
|
if !rv.CanSet() {
|
||||||
|
panic("cannot set dst")
|
||||||
|
}
|
||||||
|
|
||||||
|
isSlice := false
|
||||||
|
switch rt.Elem().Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
case reflect.Slice:
|
||||||
|
isSlice = true
|
||||||
|
default:
|
||||||
|
panic("unexpected type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if isSlice {
|
||||||
|
for _, p := range res.Returnval {
|
||||||
|
v, err := ObjectContentToType(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
vt := reflect.TypeOf(v)
|
||||||
|
|
||||||
|
if !rv.Type().AssignableTo(vt) {
|
||||||
|
// For example: dst is []ManagedEntity, res is []HostSystem
|
||||||
|
if field, ok := vt.FieldByName(rt.Elem().Elem().Name()); ok && field.Anonymous {
|
||||||
|
rv.Set(reflect.Append(rv, reflect.ValueOf(v).FieldByIndex(field.Index)))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rv.Set(reflect.Append(rv, reflect.ValueOf(v)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch len(res.Returnval) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
v, err := ObjectContentToType(res.Returnval[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
vt := reflect.TypeOf(v)
|
||||||
|
|
||||||
|
if !rv.Type().AssignableTo(vt) {
|
||||||
|
// For example: dst is ComputeResource, res is ClusterComputeResource
|
||||||
|
if field, ok := vt.FieldByName(rt.Elem().Name()); ok && field.Anonymous {
|
||||||
|
rv.Set(reflect.ValueOf(v).FieldByIndex(field.Index))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rv.Set(reflect.ValueOf(v))
|
||||||
|
default:
|
||||||
|
// If dst is not a slice, expect to receive 0 or 1 results
|
||||||
|
panic("more than 1 result")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrievePropertiesForRequest calls the RetrieveProperties method with the
|
||||||
|
// specified request and decodes the response struct into the value pointed to
|
||||||
|
// by dst.
|
||||||
|
func RetrievePropertiesForRequest(ctx context.Context, r soap.RoundTripper, req types.RetrieveProperties, dst interface{}) error {
|
||||||
|
res, err := methods.RetrieveProperties(ctx, r, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadRetrievePropertiesResponse(res, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveProperties retrieves the properties of the managed object specified
|
||||||
|
// as obj and decodes the response struct into the value pointed to by dst.
|
||||||
|
func RetrieveProperties(ctx context.Context, r soap.RoundTripper, pc, obj types.ManagedObjectReference, dst interface{}) error {
|
||||||
|
req := types.RetrieveProperties{
|
||||||
|
This: pc,
|
||||||
|
SpecSet: []types.PropertyFilterSpec{
|
||||||
|
{
|
||||||
|
ObjectSet: []types.ObjectSpec{
|
||||||
|
{
|
||||||
|
Obj: obj,
|
||||||
|
Skip: types.NewBool(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PropSet: []types.PropertySpec{
|
||||||
|
{
|
||||||
|
All: types.NewBool(true),
|
||||||
|
Type: obj.Type,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return RetrievePropertiesForRequest(ctx, r, req, dst)
|
||||||
|
}
|
|
@ -0,0 +1,247 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 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 mo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type typeInfo struct {
|
||||||
|
typ reflect.Type
|
||||||
|
|
||||||
|
// Field indices of "Self" field.
|
||||||
|
self []int
|
||||||
|
|
||||||
|
// Map property names to field indices.
|
||||||
|
props map[string][]int
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeInfoLock sync.RWMutex
|
||||||
|
var typeInfoMap = make(map[string]*typeInfo)
|
||||||
|
|
||||||
|
func typeInfoForType(tname string) *typeInfo {
|
||||||
|
typeInfoLock.RLock()
|
||||||
|
ti, ok := typeInfoMap[tname]
|
||||||
|
typeInfoLock.RUnlock()
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
return ti
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new typeInfo for type.
|
||||||
|
if typ, ok := t[tname]; !ok {
|
||||||
|
panic("unknown type: " + tname)
|
||||||
|
} else {
|
||||||
|
// Multiple routines may race to set it, but the result is the same.
|
||||||
|
typeInfoLock.Lock()
|
||||||
|
ti = newTypeInfo(typ)
|
||||||
|
typeInfoMap[tname] = ti
|
||||||
|
typeInfoLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ti
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTypeInfo(typ reflect.Type) *typeInfo {
|
||||||
|
t := typeInfo{
|
||||||
|
typ: typ,
|
||||||
|
props: make(map[string][]int),
|
||||||
|
}
|
||||||
|
|
||||||
|
t.build(typ, "", []int{})
|
||||||
|
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
var managedObjectRefType = reflect.TypeOf((*types.ManagedObjectReference)(nil)).Elem()
|
||||||
|
|
||||||
|
func buildName(fn string, f reflect.StructField) string {
|
||||||
|
if fn != "" {
|
||||||
|
fn += "."
|
||||||
|
}
|
||||||
|
|
||||||
|
motag := f.Tag.Get("mo")
|
||||||
|
if motag != "" {
|
||||||
|
return fn + motag
|
||||||
|
}
|
||||||
|
|
||||||
|
xmltag := f.Tag.Get("xml")
|
||||||
|
if xmltag != "" {
|
||||||
|
tokens := strings.Split(xmltag, ",")
|
||||||
|
if tokens[0] != "" {
|
||||||
|
return fn + tokens[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *typeInfo) build(typ reflect.Type, fn string, fi []int) {
|
||||||
|
if typ.Kind() == reflect.Ptr {
|
||||||
|
typ = typ.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ.Kind() != reflect.Struct {
|
||||||
|
panic("need struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
|
f := typ.Field(i)
|
||||||
|
ftyp := f.Type
|
||||||
|
|
||||||
|
// Copy field indices so they can be passed along.
|
||||||
|
fic := make([]int, len(fi)+1)
|
||||||
|
copy(fic, fi)
|
||||||
|
fic[len(fi)] = i
|
||||||
|
|
||||||
|
// Recurse into embedded field.
|
||||||
|
if f.Anonymous {
|
||||||
|
t.build(ftyp, fn, fic)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top level type has a "Self" field.
|
||||||
|
if f.Name == "Self" && ftyp == managedObjectRefType {
|
||||||
|
t.self = fic
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fnc := buildName(fn, f)
|
||||||
|
if fnc == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.props[fnc] = fic
|
||||||
|
|
||||||
|
// Dereference pointer.
|
||||||
|
if ftyp.Kind() == reflect.Ptr {
|
||||||
|
ftyp = ftyp.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slices are not addressable by `foo.bar.qux`.
|
||||||
|
if ftyp.Kind() == reflect.Slice {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the managed reference type.
|
||||||
|
if ftyp == managedObjectRefType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recurse into structs.
|
||||||
|
if ftyp.Kind() == reflect.Struct {
|
||||||
|
t.build(ftyp, fnc, fic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignValue assignes a value 'pv' to the struct pointed to by 'val', given a
|
||||||
|
// slice of field indices. It recurses into the struct until it finds the field
|
||||||
|
// specified by the indices. It creates new values for pointer types where
|
||||||
|
// needed.
|
||||||
|
func assignValue(val reflect.Value, fi []int, pv reflect.Value) {
|
||||||
|
// Create new value if necessary.
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
if val.IsNil() {
|
||||||
|
val.Set(reflect.New(val.Type().Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
rv := val.Field(fi[0])
|
||||||
|
fi = fi[1:]
|
||||||
|
if len(fi) == 0 {
|
||||||
|
rt := rv.Type()
|
||||||
|
pt := pv.Type()
|
||||||
|
|
||||||
|
// If type is a pointer, create new instance of type.
|
||||||
|
if rt.Kind() == reflect.Ptr {
|
||||||
|
rv.Set(reflect.New(rt.Elem()))
|
||||||
|
rv = rv.Elem()
|
||||||
|
rt = rv.Type()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If type is an interface, check if pv implements it.
|
||||||
|
if rt.Kind() == reflect.Interface && !pt.Implements(rt) {
|
||||||
|
// Check if pointer to pv implements it.
|
||||||
|
if reflect.PtrTo(pt).Implements(rt) {
|
||||||
|
npv := reflect.New(pt)
|
||||||
|
npv.Elem().Set(pv)
|
||||||
|
pv = npv
|
||||||
|
pt = pv.Type()
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintf("type %s doesn't implement %s", pt.Name(), rt.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pt.AssignableTo(rt) {
|
||||||
|
rv.Set(pv)
|
||||||
|
} else if rt.ConvertibleTo(pt) {
|
||||||
|
rv.Set(pv.Convert(rt))
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintf("cannot assign %s (%s) to %s (%s)", rt.Name(), rt.Kind(), pt.Name(), pt.Kind()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assignValue(rv, fi, pv)
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrayOfRegexp = regexp.MustCompile("ArrayOf(.*)$")
|
||||||
|
|
||||||
|
func anyTypeToValue(t interface{}) reflect.Value {
|
||||||
|
rt := reflect.TypeOf(t)
|
||||||
|
rv := reflect.ValueOf(t)
|
||||||
|
|
||||||
|
// Dereference if ArrayOfXYZ type
|
||||||
|
m := arrayOfRegexp.FindStringSubmatch(rt.Name())
|
||||||
|
if len(m) > 0 {
|
||||||
|
// ArrayOfXYZ type has single field named XYZ
|
||||||
|
rv = rv.FieldByName(m[1])
|
||||||
|
if !rv.IsValid() {
|
||||||
|
panic(fmt.Sprintf("expected %s type to have field %s", m[0], m[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadObjectFromContent loads properties from the 'PropSet' field in the
|
||||||
|
// specified ObjectContent value into the value it represents, which is
|
||||||
|
// returned as a reflect.Value.
|
||||||
|
func (t *typeInfo) LoadFromObjectContent(o types.ObjectContent) (reflect.Value, error) {
|
||||||
|
v := reflect.New(t.typ)
|
||||||
|
assignValue(v, t.self, reflect.ValueOf(o.Obj))
|
||||||
|
|
||||||
|
for _, p := range o.PropSet {
|
||||||
|
rv, ok := t.props[p.Name]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
assignValue(v, rv, anyTypeToValue(p.Val))
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, nil
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 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 "sync"
|
||||||
|
|
||||||
|
type Aggregator struct {
|
||||||
|
downstream Sinker
|
||||||
|
upstream chan (<-chan Report)
|
||||||
|
|
||||||
|
done chan struct{}
|
||||||
|
w sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAggregator(s Sinker) *Aggregator {
|
||||||
|
a := &Aggregator{
|
||||||
|
downstream: s,
|
||||||
|
upstream: make(chan (<-chan Report)),
|
||||||
|
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
a.w.Add(1)
|
||||||
|
go a.loop()
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Aggregator) loop() {
|
||||||
|
defer a.w.Done()
|
||||||
|
|
||||||
|
dch := a.downstream.Sink()
|
||||||
|
defer close(dch)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case uch := <-a.upstream:
|
||||||
|
// Drain upstream channel
|
||||||
|
for e := range uch {
|
||||||
|
dch <- e
|
||||||
|
}
|
||||||
|
case <-a.done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Aggregator) Sink() chan<- Report {
|
||||||
|
ch := make(chan Report)
|
||||||
|
a.upstream <- ch
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done marks the aggregator as done. No more calls to Sink() may be made and
|
||||||
|
// the downstream progress report channel will be closed when Done() returns.
|
||||||
|
func (a *Aggregator) Done() {
|
||||||
|
close(a.done)
|
||||||
|
a.w.Wait()
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 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
|
||||||
|
|
||||||
|
/*
|
||||||
|
The progress package contains functionality to deal with progress reporting.
|
||||||
|
The functionality is built to serve progress reporting for infrastructure
|
||||||
|
operations when talking the vSphere API, but is generic enough to be used
|
||||||
|
elsewhere.
|
||||||
|
|
||||||
|
At the core of this progress reporting API lies the Sinker interface. This
|
||||||
|
interface is implemented by any object that can act as a sink for progress
|
||||||
|
reports. Callers of the Sink() function receives a send-only channel for
|
||||||
|
progress reports. They are responsible for closing the channel when done.
|
||||||
|
This semantic makes it easy to keep track of multiple progress report channels;
|
||||||
|
they are only created when Sink() is called and assumed closed when any
|
||||||
|
function that receives a Sinker parameter returns.
|
||||||
|
*/
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 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"
|
||||||
|
|
||||||
|
type prefixedReport struct {
|
||||||
|
Report
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r prefixedReport) Detail() string {
|
||||||
|
if d := r.Report.Detail(); d != "" {
|
||||||
|
return fmt.Sprintf("%s: %s", r.prefix, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
func prefixLoop(upstream <-chan Report, downstream chan<- Report, prefix string) {
|
||||||
|
defer close(downstream)
|
||||||
|
|
||||||
|
for r := range upstream {
|
||||||
|
downstream <- prefixedReport{
|
||||||
|
Report: r,
|
||||||
|
prefix: prefix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Prefix(s Sinker, prefix string) Sinker {
|
||||||
|
fn := func() chan<- Report {
|
||||||
|
upstream := make(chan Report)
|
||||||
|
downstream := s.Sink()
|
||||||
|
go prefixLoop(upstream, downstream, prefix)
|
||||||
|
return upstream
|
||||||
|
}
|
||||||
|
|
||||||
|
return SinkFunc(fn)
|
||||||
|
}
|
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014-2015 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 (
|
||||||
|
"container/list"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type readerReport struct {
|
||||||
|
t time.Time
|
||||||
|
|
||||||
|
pos int64
|
||||||
|
size int64
|
||||||
|
bps *uint64
|
||||||
|
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r readerReport) Percentage() float32 {
|
||||||
|
return 100.0 * float32(r.pos) / float32(r.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r readerReport) Detail() string {
|
||||||
|
const (
|
||||||
|
KiB = 1024
|
||||||
|
MiB = 1024 * KiB
|
||||||
|
GiB = 1024 * MiB
|
||||||
|
)
|
||||||
|
|
||||||
|
// Use the reader's bps field, so this report returns an up-to-date number.
|
||||||
|
//
|
||||||
|
// For example: if there hasn't been progress for the last 5 seconds, the
|
||||||
|
// most recent report should return "0B/s".
|
||||||
|
//
|
||||||
|
bps := atomic.LoadUint64(r.bps)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case bps >= GiB:
|
||||||
|
return fmt.Sprintf("%.1fGiB/s", float32(bps)/float32(GiB))
|
||||||
|
case bps >= MiB:
|
||||||
|
return fmt.Sprintf("%.1fMiB/s", float32(bps)/float32(MiB))
|
||||||
|
case bps >= KiB:
|
||||||
|
return fmt.Sprintf("%.1fKiB/s", float32(bps)/float32(KiB))
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%dB/s", bps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p readerReport) Error() error {
|
||||||
|
return p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// reader wraps an io.Reader and sends a progress report over a channel for
|
||||||
|
// every read it handles.
|
||||||
|
type reader struct {
|
||||||
|
r io.Reader
|
||||||
|
|
||||||
|
pos int64
|
||||||
|
size int64
|
||||||
|
|
||||||
|
bps uint64
|
||||||
|
|
||||||
|
ch chan<- Report
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReader(s Sinker, r io.Reader, size int64) *reader {
|
||||||
|
pr := reader{
|
||||||
|
r: r,
|
||||||
|
|
||||||
|
size: size,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reports must be sent downstream and to the bps computation loop.
|
||||||
|
pr.ch = Tee(s, newBpsLoop(&pr.bps)).Sink()
|
||||||
|
|
||||||
|
return &pr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read calls the Read function on the underlying io.Reader. Additionally,
|
||||||
|
// every read causes a progress report to be sent to the progress reader's
|
||||||
|
// underlying channel.
|
||||||
|
func (r *reader) Read(b []byte) (int, error) {
|
||||||
|
n, err := r.r.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.pos += int64(n)
|
||||||
|
q := readerReport{
|
||||||
|
t: time.Now(),
|
||||||
|
pos: r.pos,
|
||||||
|
size: r.size,
|
||||||
|
bps: &r.bps,
|
||||||
|
}
|
||||||
|
|
||||||
|
r.ch <- q
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done marks the progress reader as done, optionally including an error in the
|
||||||
|
// progress report. After sending it, the underlying channel is closed.
|
||||||
|
func (r *reader) Done(err error) {
|
||||||
|
q := readerReport{
|
||||||
|
t: time.Now(),
|
||||||
|
pos: r.pos,
|
||||||
|
size: r.size,
|
||||||
|
bps: &r.bps,
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
|
||||||
|
r.ch <- q
|
||||||
|
close(r.ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBpsLoop returns a sink that monitors and stores throughput.
|
||||||
|
func newBpsLoop(dst *uint64) SinkFunc {
|
||||||
|
fn := func() chan<- Report {
|
||||||
|
sink := make(chan Report)
|
||||||
|
go bpsLoop(sink, dst)
|
||||||
|
return sink
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpsLoop(ch <-chan Report, dst *uint64) {
|
||||||
|
l := list.New()
|
||||||
|
|
||||||
|
for {
|
||||||
|
var tch <-chan time.Time
|
||||||
|
|
||||||
|
// Setup timer for front of list to become stale.
|
||||||
|
if e := l.Front(); e != nil {
|
||||||
|
dt := time.Second - time.Now().Sub(e.Value.(readerReport).t)
|
||||||
|
tch = time.After(dt)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case q, ok := <-ch:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.PushBack(q)
|
||||||
|
case <-tch:
|
||||||
|
l.Remove(l.Front())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute new bps
|
||||||
|
if l.Len() == 0 {
|
||||||
|
atomic.StoreUint64(dst, 0)
|
||||||
|
} else {
|
||||||
|
f := l.Front().Value.(readerReport)
|
||||||
|
b := l.Back().Value.(readerReport)
|
||||||
|
atomic.StoreUint64(dst, uint64(b.pos-f.pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 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
|
||||||
|
|
||||||
|
// Report defines the interface for types that can deliver progress reports.
|
||||||
|
// Examples include uploads/downloads in the http client and the task info
|
||||||
|
// field in the task managed object.
|
||||||
|
type Report interface {
|
||||||
|
Percentage() float32
|
||||||
|
Detail() string
|
||||||
|
Error() error
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 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
|
||||||
|
|
||||||
|
type scaledReport struct {
|
||||||
|
Report
|
||||||
|
n int
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r scaledReport) Percentage() float32 {
|
||||||
|
b := 100 * float32(r.i) / float32(r.n)
|
||||||
|
return b + (r.Report.Percentage() / float32(r.n))
|
||||||
|
}
|
||||||
|
|
||||||
|
type scaleOne struct {
|
||||||
|
s Sinker
|
||||||
|
n int
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaleOne) Sink() chan<- Report {
|
||||||
|
upstream := make(chan Report)
|
||||||
|
downstream := s.s.Sink()
|
||||||
|
go s.loop(upstream, downstream)
|
||||||
|
return upstream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaleOne) loop(upstream <-chan Report, downstream chan<- Report) {
|
||||||
|
defer close(downstream)
|
||||||
|
|
||||||
|
for r := range upstream {
|
||||||
|
downstream <- scaledReport{
|
||||||
|
Report: r,
|
||||||
|
n: s.n,
|
||||||
|
i: s.i,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type scaleMany struct {
|
||||||
|
s Sinker
|
||||||
|
n int
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func Scale(s Sinker, n int) Sinker {
|
||||||
|
return &scaleMany{
|
||||||
|
s: s,
|
||||||
|
n: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scaleMany) Sink() chan<- Report {
|
||||||
|
if s.i == s.n {
|
||||||
|
s.n++
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := scaleOne{s: s.s, n: s.n, i: s.i}.Sink()
|
||||||
|
s.i++
|
||||||
|
return ch
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue