diff --git a/.travis.yml b/.travis.yml index 3e1b44ec..ad255a43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,28 +1,27 @@ language: go go: - - 1.6 - - 1.7 + - 1.9 - tip os: - linux - osx -env: - matrix: - - BUILD_GOARCH=amd64 - - BUILD_GOARCH=386 +matrix: + allow_failures: + - go: tip + fast_finish: true # don't wait for tip tests to finish before marking build "succeeded" -before_install: - - go get -u honnef.co/go/simple/cmd/gosimple - -# don't go get deps. will only build with code in vendor directory. +# skip the install step. don't `go get` deps. only build with code in vendor/ install: true +before_script: + - go get honnef.co/go/tools/cmd/megacheck + - GO_FILES=$(find . -iname '*.go' -type f | grep -v /vendor/) + script: - # pkgs avoids testing anything in vendor/ - - pkgs=$(go list ./... | grep -v /vendor/) - - go vet $pkgs - - go test -v -race $pkgs - - gosimple $pkgs + - test -z $(gofmt -s -l $GO_FILES) + - go vet ./... + - megacheck ./... + - go test -v -race $PKGS diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 00000000..9971cf87 --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,117 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + name = "cloud.google.com/go" + packages = ["compute/metadata"] + revision = "5a9e19d4e1e41a734154e44a2132b358afb49a03" + version = "v0.13.0" + +[[projects]] + name = "github.com/Azure/azure-sdk-for-go" + packages = ["arm/compute","arm/network","arm/resources/resources","arm/storage","management","management/hostedservice","management/location","management/virtualmachine","management/virtualmachinedisk","management/virtualnetwork","management/vmutils","storage"] + revision = "df4dd90d076ebbf6e87d08d3f00bfac8ff4bde1a" + version = "v10.3.1-beta" + +[[projects]] + name = "github.com/Azure/go-autorest" + packages = ["autorest","autorest/adal","autorest/azure","autorest/date","autorest/to","autorest/validation"] + revision = "5432abe734f8d95c78340cd56712f912906e6514" + version = "v8.3.1" + +[[projects]] + branch = "master" + name = "github.com/apcera/util" + packages = ["uuid"] + revision = "346d8c10c6f817df60ff768b05ced4170d747180" + +[[projects]] + name = "github.com/aws/aws-sdk-go" + packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","internal/shareddefaults","private/protocol","private/protocol/ec2query","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/xml/xmlutil","service/ec2","service/sts"] + revision = "54cc1aa0ef5ada10597b09e1939a8eb75f6aef96" + version = "v1.10.43" + +[[projects]] + name = "github.com/dgrijalva/jwt-go" + packages = ["."] + revision = "d2709f9f1f31ebcda9651b03077758c1f3a0018c" + version = "v3.0.0" + +[[projects]] + name = "github.com/go-ini/ini" + packages = ["."] + revision = "20b96f641a5ea98f2f8619ff4f3e061cff4833bd" + version = "v1.28.2" + +[[projects]] + branch = "master" + name = "github.com/golang/protobuf" + packages = ["proto"] + revision = "17ce1425424ab154092bbb43af630bd647f3bb0d" + +[[projects]] + branch = "master" + name = "github.com/gophercloud/gophercloud" + packages = [".","openstack","openstack/blockstorage/v1/volumes","openstack/compute/v2/extensions/floatingips","openstack/compute/v2/extensions/startstop","openstack/compute/v2/extensions/volumeattach","openstack/compute/v2/flavors","openstack/compute/v2/images","openstack/compute/v2/servers","openstack/identity/v2/tenants","openstack/identity/v2/tokens","openstack/identity/v3/tokens","openstack/networking/v2/networks","openstack/utils","pagination"] + revision = "e764e6cf0316a135851b56f8eea946b906988707" + +[[projects]] + name = "github.com/jmespath/go-jmespath" + packages = ["."] + revision = "3433f3ea46d9f8019119e7dd41274e112a2359a9" + version = "0.2.2" + +[[projects]] + branch = "master" + name = "github.com/pyr/egoscale" + packages = ["."] + revision = "325740036187ddae3a5b74be00fbbc70011c4d96" + +[[projects]] + name = "github.com/satori/uuid" + packages = ["."] + revision = "879c5887cd475cd7864858769793b2ceb0d44feb" + version = "v1.1.0" + +[[projects]] + branch = "master" + name = "github.com/vmware/govmomi" + packages = [".","find","list","nfc","object","ovf","property","session","task","vim25","vim25/debug","vim25/methods","vim25/mo","vim25/progress","vim25/soap","vim25/types","vim25/xml"] + revision = "17b8c9ccb7f8c7b015d44c4ea39305c970a7bf31" + +[[projects]] + branch = "master" + name = "golang.org/x/crypto" + packages = ["curve25519","ed25519","ed25519/internal/edwards25519","pkcs12","pkcs12/internal/rc2","ssh"] + revision = "faadfbdc035307d901e69eea569f5dda451a3ee3" + +[[projects]] + branch = "master" + name = "golang.org/x/net" + packages = ["context","context/ctxhttp"] + revision = "66aacef3dd8a676686c7ae3716979581e8b03c47" + +[[projects]] + branch = "master" + name = "golang.org/x/oauth2" + packages = [".","google","internal","jws","jwt"] + revision = "d89af98d7c6bba047c5a2622f36bc14b8766df85" + +[[projects]] + branch = "master" + name = "google.golang.org/api" + packages = ["compute/v1","gensupport","googleapi","googleapi/internal/uritemplates"] + revision = "6d17a978cb363067e40a571c7cd7ddc010af35b0" + +[[projects]] + name = "google.golang.org/appengine" + packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"] + revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" + version = "v1.0.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "f4c60a26d2f23a002094604b4faabfe170b7136df9f5a264eeede7bf1b3246ec" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 00000000..41dde688 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,40 @@ + +[[constraint]] + name = "github.com/Azure/azure-sdk-for-go" + version = "10.3.1-beta" + +[[constraint]] + name = "github.com/Azure/go-autorest" + version = "8.3.1" + +[[constraint]] + branch = "master" + name = "github.com/apcera/util" + +[[constraint]] + name = "github.com/aws/aws-sdk-go" + version = "1.10.43" + +[[constraint]] + branch = "master" + name = "github.com/gophercloud/gophercloud" + +[[constraint]] + branch = "master" + name = "github.com/pyr/egoscale" + +[[constraint]] + branch = "master" + name = "github.com/vmware/govmomi" + +[[constraint]] + branch = "master" + name = "golang.org/x/crypto" + +[[constraint]] + branch = "master" + name = "golang.org/x/oauth2" + +[[constraint]] + branch = "master" + name = "google.golang.org/api" diff --git a/README.md b/README.md index 9e163c8a..77412805 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,11 @@ Supported Providers Getting Started ================ -Go version 1.6+ is required. +Go version 1.9+ is required. -`go get github.com/apcera/libretto/...` - -`go build ./...` +``` sh +go get -u github.com/apcera/libretto +``` Examples ========= @@ -40,75 +40,73 @@ AWS ---- ``` go - rawKey, err := ioutil.ReadFile(*key) if err != nil { - return err + return err } vm := &aws.VM{ - Name: "libretto-aws", - AMI: "ami-984734", - InstanceType: "m4.large", - SSHCreds: ssh.Credentials{ - SSHUser: "ubuntu", - SSHPrivateKey: string(rawKey), - }, - Volumes: []aws.EBSVolume{ - { - DeviceName: "/dev/sda1", - }, - }, - Region: "ap-northeast-1", - KeyPair: strings.TrimSuffix(filepath.Base(*key), filepath.Ext(*key)), - SecurityGroup: "sg-9fdsfds", + Name: "libretto-aws", + AMI: "ami-984734", + InstanceType: "m4.large", + SSHCreds: ssh.Credentials{ + SSHUser: "ubuntu", + SSHPrivateKey: string(rawKey), + }, + Volumes: []aws.EBSVolume{ + { + DeviceName: "/dev/sda1", + }, + }, + Region: "ap-northeast-1", + KeyPair: strings.TrimSuffix(filepath.Base(*key), filepath.Ext(*key)), + SecurityGroup: "sg-9fdsfds", } -if err := aws.ValidCredentials(vm.Region); err != nil { - return err +err = aws.ValidCredentials(vm.Region) +if err != nil { + return err } -if err := vm.Provision(); err != nil { - return err +err = vm.Provision() +if err != nil { + return err } - ``` Azure ------ ``` go - vm := &azure.VM{ - Creds: azure.OAuthCredentials{ - SubscriptionID: os.Getenv("AZURE_SUBSCRIPTION_ID"), - TenantID: os.Getenv("AZURE_TENANT_ID"), - ClientID: os.Getenv("AZURE_CLIENT_ID"), - ClientSecret: os.Getenv("AZURE_CLIENT_SECRET"), - }, - ImagePublisher: "Canonical", - ImageOffer: "UbuntuServer", - ImageSku: "14.04.3-LTS", - Size: "Standard_A1", - DiskSize: 40 // Set to 0 if you don't want to attach any additional disk - Name: "libretto", - ResourceGroup: "libretto-rg", - StorageAccount: "libretto-sa", - StorageContainer: "libretto-sc", - SSHCreds: ssh.Credentials{ - SSHUser: os.Getenv("AZURE_USER"), - SSHPassword: os.Getenv("AZURE_PASSWORD"), - }, - - NetworkSecurityGroup: "libretto-sg", - VirtualNetwork: "libretto-vn", - Subnet: "libretto-sn", + Creds: azure.OAuthCredentials{ + SubscriptionID: os.Getenv("AZURE_SUBSCRIPTION_ID"), + TenantID: os.Getenv("AZURE_TENANT_ID"), + ClientID: os.Getenv("AZURE_CLIENT_ID"), + ClientSecret: os.Getenv("AZURE_CLIENT_SECRET"), + }, + ImagePublisher: "Canonical", + ImageOffer: "UbuntuServer", + ImageSku: "14.04.3-LTS", + Size: "Standard_A1", + DiskSize: 40, // Set to 0 if you don't want to attach any additional disk + Name: "libretto", + ResourceGroup: "libretto-rg", + StorageAccount: "libretto-sa", + StorageContainer: "libretto-sc", + SSHCreds: ssh.Credentials{ + SSHUser: os.Getenv("AZURE_USER"), + SSHPassword: os.Getenv("AZURE_PASSWORD"), + }, + + NetworkSecurityGroup: "libretto-sg", + VirtualNetwork: "libretto-vn", + Subnet: "libretto-sn", } if err := vm.Provision(); err != nil { - return err + return err } - ``` Digital Ocean @@ -117,178 +115,171 @@ Digital Ocean ``` go token := os.Getenv("DIGITALOCEAN_API_KEY") if token == "" { - return fmt.Errorf("Please export your DigitalOcean API key to 'DIGITALOCEAN_API_KEY' and run again.") + return fmt.Errorf("Please export your DigitalOcean API key to 'DIGITALOCEAN_API_KEY' and run again.") } config := digitalocean.Config{ - Name: defaultDropletName, - Region: defaultDropletRegion, - Image: defaultDropletImage, - Size: defaultDropletSize, + Name: defaultDropletName, + Region: defaultDropletRegion, + Image: defaultDropletImage, + Size: defaultDropletSize, } vm := digitalocean.VM{ - APIToken: token, - Config: config, + APIToken: token, + Config: config, } if err := vm.Provision(); err != nil { - return err + return err } ``` Exoscale --------- - ``` go - - vm := exoscale.VM{ - Config: exoscale.Config{ - Endpoint: "https://api.exoscale.ch/compute", - APIKey: os.Getenv("EXOSCALE_API_KEY"), - APISecret: os.Getenv("EXOSCALE_API_SECRET"), - }, - Template: exoscale.Template{ - Name: "Linux Ubuntu 16.04 LTS 64-bit", - StorageGB: 10, - ZoneName: "ch-dk-2", - }, - ServiceOffering: exoscale.ServiceOffering{ - Name: exoscale.Medium, - }, - SecurityGroups: []exoscale.SecurityGroup{ - {Name: "default"}, - {Name: "my sg"}, - }, - Zone: exoscale.Zone{ - Name: "ch-dk-2", - }, - KeypairName: "mydev", - Name: "libretto-exoscale", - Userdata: ` -#cloud-config +``` go +vm := exoscale.VM{ + Config: exoscale.Config{ + Endpoint: "https://api.exoscale.ch/compute", + APIKey: os.Getenv("EXOSCALE_API_KEY"), + APISecret: os.Getenv("EXOSCALE_API_SECRET"), + }, + Template: exoscale.Template{ + Name: "Linux Ubuntu 16.04 LTS 64-bit", + StorageGB: 10, + ZoneName: "ch-dk-2", + }, + ServiceOffering: exoscale.ServiceOffering{ + Name: exoscale.Medium, + }, + SecurityGroups: []exoscale.SecurityGroup{ + {Name: "default"}, + {Name: "my sg"}, + }, + Zone: exoscale.Zone{ + Name: "ch-dk-2", + }, + KeypairName: "mydev", + Name: "libretto-exoscale", + Userdata: ` +# cloud-config manage_etc_hosts: true fqdn: new.host `, - } - - if err := vm.Provision(); err != nil { - fmt.Printf("Error provisioning machine: %s\n", err) - } +} - // get VM ID polling every 5 seconds, timeout after 30 - vm.WaitVMCreation(30, 5) +if err := vm.Provision(); err != nil { + fmt.Printf("Error provisioning machine: %s\n", err) +} - ``` +// get VM ID polling every 5 seconds, timeout after 30 +vm.WaitVMCreation(30, 5) +``` Google Cloud Platform ---------- ``` go - vm := &gcp.VM{ - Name: "libretto-vm-1", - Zone: "us-central1-a", - MachineType: "n1-standard-1", - SourceImage: "ubuntu-1404-trusty-v20160516", - Disks: []gcp.Disk{ - { - DiskType: "pd-standard", - DiskSizeGb: 10, - AutoDelete: true, - }, - { - DiskType: "pd-standard", - DiskSizeGb: 500, - AutoDelete: true, - Name: "addtional-disk", - }, - }, - Preemptible: false, - Network: "default", - Subnetwork: "default", - UseInternalIP: false, - Project: os.Getenv("YOUR-GOOGLE-PROJECT"), - Scopes: scopes, - - AccountFile: accountFile, - SSHCreds: ssh.Credentials{ - SSHUser: "ubuntu", - SSHPrivateKey: string(sshPrivateKey), - }, - SSHPublicKey: string(sshPublicKey), - Tags: []string{"libretto"}, - } - - err := vm.Provision() - if err != nil { - return err - } - ``` + Name: "libretto-vm-1", + Zone: "us-central1-a", + MachineType: "n1-standard-1", + SourceImage: "ubuntu-1404-trusty-v20160516", + Disks: []gcp.Disk{ + { + DiskType: "pd-standard", + DiskSizeGb: 10, + AutoDelete: true, + }, + { + DiskType: "pd-standard", + DiskSizeGb: 500, + AutoDelete: true, + Name: "addtional-disk", + }, + }, + Preemptible: false, + Network: "default", + Subnetwork: "default", + UseInternalIP: false, + Project: os.Getenv("YOUR-GOOGLE-PROJECT"), + Scopes: scopes, + + AccountFile: accountFile, + SSHCreds: ssh.Credentials{ + SSHUser: "ubuntu", + SSHPrivateKey: string(sshPrivateKey), + }, + SSHPublicKey: string(sshPublicKey), + Tags: []string{"libretto"}, +} + +if err := vm.Provision(); err != nil { + return err +} +``` Openstack ---------- ``` go +metadata := openstack.NewDefaultImageMetadata() +volume := openstack.NewDefaultVolume() +// Optional +cloudInit, err := ioutil.ReadFile("/your/user/data") +if err != nil { + return err +} - metadata := openstack.NewDefaultImageMetadata() - volume := openstack.NewDefaultVolume() - - // Optional - cloudInit, err := ioutil.ReadFile("/your/user/data") - if err != nil { - return err - } - - vm := &openstack.VM{ - IdentityEndpoint: os.Getenv("OS_AUTH_URL"), - Username: os.Getenv("OS_USERNAME"), - Password: os.Getenv("OS_PASSWORD"), - Region: os.Getenv("OS_REGION_NAME"), - TenantName: os.Getenv("OS_TENANT_NAME"), - FlavorName: "m1.medium", - ImageID: "", - ImageMetadata: metadata, - ImagePath: os.Getenv("OS_IMAGE_PATH"), - Volume: volume, - InstanceID: "", - Name: "test", - Networks: []string{"eab29109-3363-4b03-8a56-8fe27b71f3a0"}, - FloatingIPPool: "net04_ext", - FloatingIP: nil, - SecurityGroup: "test", - AdminPassword: os.Getenv("OS_ADMIN_PASSWORD"), - UserData: cloudInit, - Credentials: ssh.Credentials{ - SSHUser: "ubuntu", - SSHPassword: "ubuntu", - }, - } - - err := vm.Provision() - if err != nil { - return err - } - ``` +vm := &openstack.VM{ + IdentityEndpoint: os.Getenv("OS_AUTH_URL"), + Username: os.Getenv("OS_USERNAME"), + Password: os.Getenv("OS_PASSWORD"), + Region: os.Getenv("OS_REGION_NAME"), + TenantName: os.Getenv("OS_TENANT_NAME"), + FlavorName: "m1.medium", + ImageID: "", + ImageMetadata: metadata, + ImagePath: os.Getenv("OS_IMAGE_PATH"), + Volume: volume, + InstanceID: "", + Name: "test", + Networks: []string{"eab29109-3363-4b03-8a56-8fe27b71f3a0"}, + FloatingIPPool: "net04_ext", + FloatingIP: nil, + SecurityGroup: "test", + AdminPassword: os.Getenv("OS_ADMIN_PASSWORD"), + UserData: cloudInit, + Credentials: ssh.Credentials{ + SSHUser: "ubuntu", + SSHPassword: "ubuntu", + }, +} + +err = vm.Provision() +if err != nil { + return err +} +``` Virtualbox ----------- ``` go - var config virtualbox.Config config.NICs = []virtualbox.NIC{ - virtualbox.NIC{Idx: 1, Backing: virtualbox.Bridged, BackingDevice: "en0: Wi-Fi (AirPort)"}, + virtualbox.NIC{Idx: 1, Backing: virtualbox.Bridged, BackingDevice: "en0: Wi-Fi (AirPort)"}, } vm := virtualbox.VM{Src: "/Users/Admin/vm-bfb21a62-60c5-11e5-9fc5-a45e60e45ad5.ova", - Credentials: ssh.Credentials{ - SSHUser: "ubuntu", - SSHPassword: "ubuntu", - }, - Config: config, + Credentials: ssh.Credentials{ + SSHUser: "ubuntu", + SSHPassword: "ubuntu", + }, + Config: config, } if err := vm.Provision(); err != nil { - return err + return err } ``` @@ -296,31 +287,30 @@ vSphere -------- ``` go - vm := &vsphere.VM{ - Host: "10.2.1.11", - Username: "username", - Password: "password", - Datacenter: "test-dc", - Datastores: []string{"datastore1", "datastore2"}, - Networks: map[string]string{"nat": "network1"}, - Credentials: ssh.Credentials{ - SSHUser: "ubuntu", - SSHPassword: "ubuntu", - }, - SkipExisting: true, - Insecure: true, - Destination: vsphere.Destination{ - DestinationName: "Host1", - DestinationType: "host", - HostSystem: "", - }, - Name: "test-vm", - Template: "test-template", - OvfPath: "/Users/Test/Downloads/file.ovf", + Host: "10.2.1.11", + Username: "username", + Password: "password", + Datacenter: "test-dc", + Datastores: []string{"datastore1", "datastore2"}, + Networks: map[string]string{"nat": "network1"}, + Credentials: ssh.Credentials{ + SSHUser: "ubuntu", + SSHPassword: "ubuntu", + }, + SkipExisting: true, + Insecure: true, + Destination: vsphere.Destination{ + DestinationName: "Host1", + DestinationType: "host", + HostSystem: "", + }, + Name: "test-vm", + Template: "test-template", + OvfPath: "/Users/Test/Downloads/file.ovf", } if err := vm.Provision(); err != nil { - return err + return err } ``` @@ -331,18 +321,18 @@ VMware Fusion/Workstation (vmrun) ``` go var config vmrun.Config config.NICs = []vmrun.NIC{ - vmrun.NIC{Idx: 0, Backing: vmrun.Nat, BackingDevice: "en0"}, + vmrun.NIC{Idx: 0, Backing: vmrun.Nat, BackingDevice: "en0"}, } vm := vmrun.VM{Src: "/Users/Admin/vmware_desktop/trusty-orchestrator-dev.vmx", - Dst: "/Users/Admin/Documents/VMs", - Credentials: ssh.Credentials{ - SSHUser: "ubuntu", - SSHPassword: "ubuntu", - }, - Config: config, + Dst: "/Users/Admin/Documents/VMs", + Credentials: ssh.Credentials{ + SSHUser: "ubuntu", + SSHPassword: "ubuntu", + }, + Config: config, } if err := vm.Provision(); err != nil { - return err + return err } ``` @@ -352,8 +342,8 @@ FAQ * Why write Libretto? -We couldn't find a suitable Golang binding for this functionality, so we -created this library. There are a couple of similar libraries but not in golang +We couldn't find a suitable Golang binding for this functionality, so we created +this library. There are a couple of similar libraries but not in golang (fog.io in ruby, jcloud in java, libcloud in python). Docker Machine is an effort toward that direction, but it is very Docker @@ -362,14 +352,13 @@ number of parameters, but reduces the flexibility of the tool. * What is the scope for Libretto? -Virtual machine creation and life cycle -management as well as common configuration steps during a deploy such as -configuring SSH keys. +Virtual machine creation and life cycle management as well as common +configuration steps during a deploy such as configuring SSH keys. * Why use this library over other tools? -Actively used and developed. Can be called natively from Go in a Go application -instead of shelling out to other tools. +Can be called natively from Go in a Go application instead of shelling out to +other tools. Known Issues ============= @@ -398,8 +387,8 @@ Libretto `VirtualMachine` interface. The provider should work at the minimum on the Linux, Windows and OS X platforms unless it is a platform specific provider in which case it should at least compile and return a descriptive error. -Dependencies should be versioned and stored using `gvt` -(https://github.com/FiloSottile/gvt) +Dependencies should be vendored using [`dep ensure`](https://github.com/golang/dep), +followed by [`dep prune`](https://github.com/golang/dep). Errors should be lower case so that they can be wrapped by the calling code. If possible, types defined in the top level `virtualmachine` package should be diff --git a/libretto.jpg b/libretto.jpg index fb867c6f..6e18b250 100644 Binary files a/libretto.jpg and b/libretto.jpg differ diff --git a/ssh/mock_ssh.go b/ssh/mock_ssh.go index ab9118bb..18923dd9 100644 --- a/ssh/mock_ssh.go +++ b/ssh/mock_ssh.go @@ -7,6 +7,22 @@ import ( "time" ) +// MockSSHClient represents a Mock Client wrapper. +type MockSSHClient struct { + MockConnect func() error + MockDisconnect func() + MockDownload func(src io.WriteCloser, dst string) error + MockRun func(command string, stdout io.Writer, stderr io.Writer) error + MockUpload func(src io.Reader, dst string, size int, mode uint32) error + MockValidate func() error + MockWaitForSSH func(maxWait time.Duration) error + + MockSetSSHPrivateKey func(string) + MockGetSSHPrivateKey func() string + MockSetSSHPassword func(string) + MockGetSSHPassword func() string +} + // Connect calls the mocked connect. func (c *MockSSHClient) Connect() error { if c.MockConnect != nil { @@ -19,9 +35,7 @@ func (c *MockSSHClient) Connect() error { func (c *MockSSHClient) Disconnect() { if c.MockDisconnect != nil { c.MockDisconnect() - return } - return } // Download calls the mocked download. @@ -41,9 +55,9 @@ func (c *MockSSHClient) Run(command string, stdout io.Writer, stderr io.Writer) } // Upload calls the mocked upload -func (c *MockSSHClient) Upload(src io.Reader, dst string, mode uint32) error { +func (c *MockSSHClient) Upload(src io.Reader, dst string, size int, mode uint32) error { if c.MockUpload != nil { - return c.MockUpload(src, dst, mode) + return c.MockUpload(src, dst, size, mode) } return ErrNotImplemented } @@ -68,9 +82,7 @@ func (c *MockSSHClient) WaitForSSH(maxWait time.Duration) error { func (c *MockSSHClient) SetSSHPrivateKey(s string) { if c.MockSetSSHPrivateKey != nil { c.MockSetSSHPrivateKey(s) - return } - return } // GetSSHPrivateKey calls the mocked GetSSHPrivateKey @@ -85,9 +97,7 @@ func (c *MockSSHClient) GetSSHPrivateKey() string { func (c *MockSSHClient) SetSSHPassword(s string) { if c.MockSetSSHPassword != nil { c.MockSetSSHPassword(s) - return } - return } // GetSSHPassword calls the mocked GetSSHPassword diff --git a/ssh/ssh.go b/ssh/ssh.go index 1b535e5e..fc4496d9 100644 --- a/ssh/ssh.go +++ b/ssh/ssh.go @@ -4,11 +4,9 @@ package ssh import ( "bufio" - "bytes" "errors" "fmt" "io" - "io/ioutil" "net" "os" "path" @@ -39,8 +37,6 @@ var ( ErrUnableToWriteFile = errors.New("Unable to write file") // ErrNotImplemented is returned when a function is not implemented (typically by the Mock implementation). ErrNotImplemented = errors.New("Operation not implemented") - // Setup a mutex for the close channel for thread safety. - closeMutex sync.Mutex ) const ( @@ -62,7 +58,7 @@ type Client interface { Disconnect() Download(src io.WriteCloser, dst string) error Run(command string, stdout io.Writer, stderr io.Writer) error - Upload(src io.Reader, dst string, mode uint32) error + Upload(src io.Reader, dst string, size int, mode uint32) error Validate() error WaitForSSH(maxWait time.Duration) error @@ -98,63 +94,6 @@ type SSHClient struct { close chan bool } -// MockSSHClient represents a Mock Client wrapper. -type MockSSHClient struct { - MockConnect func() error - MockDisconnect func() - MockDownload func(src io.WriteCloser, dst string) error - MockRun func(command string, stdout io.Writer, stderr io.Writer) error - MockUpload func(src io.Reader, dst string, mode uint32) error - MockValidate func() error - MockWaitForSSH func(maxWait time.Duration) error - - MockSetSSHPrivateKey func(string) - MockGetSSHPrivateKey func() string - MockSetSSHPassword func(string) - MockGetSSHPassword func() string -} - -// dial will attempt to connect to an SSH server. -var dial = func(network, addr string, config *cssh.ClientConfig) (*cssh.Client, error) { - d := net.Dialer{Timeout: Timeout, KeepAlive: 2 * time.Second} - - conn, err := d.Dial(network, addr) - if err != nil { - return nil, err - } - - c, chans, reqs, err := cssh.NewClientConn(conn, addr, config) - if err != nil { - return nil, err - } - - return cssh.NewClient(c, chans, reqs), nil -} - -var readPrivateKey = func(key string) (cssh.AuthMethod, error) { - signer, err := cssh.ParsePrivateKey([]byte(key)) - if err != nil { - return nil, err - } - - return cssh.PublicKeys(signer), nil -} - -var getAuth = func(c *Credentials, authType string) (cssh.AuthMethod, error) { - var ( - auth cssh.AuthMethod - err error - ) - - switch authType { - case PasswordAuth: - return cssh.Password(c.SSHPassword), nil - case KeyAuth: - return readPrivateKey(c.SSHPrivateKey) - } - return auth, err -} - // Connect connects to a machine using SSH. func (client *SSHClient) Connect() error { var ( @@ -183,6 +122,7 @@ func (client *SSHClient) Connect() error { Auth: []cssh.AuthMethod{ auth, }, + HostKeyCallback: cssh.InsecureIgnoreHostKey(), } port := sshPort @@ -197,12 +137,8 @@ func (client *SSHClient) Connect() error { client.cryptoClient = c - closeMutex.Lock() - defer closeMutex.Unlock() + client.close = make(chan bool, 1) - if client.close == nil { - client.close = make(chan bool, 1) - } if client.Options.KeepAlive > 0 { go client.keepAlive() } @@ -226,17 +162,7 @@ func (client *SSHClient) keepAlive() { // Disconnect should be called when the ssh client is no longer needed, and state can be cleaned up func (client *SSHClient) Disconnect() { - select { - case <-client.close: - default: - closeMutex.Lock() - defer closeMutex.Unlock() - - if client.close != nil { - close(client.close) - client.close = nil - } - } + client.close <- true } // Download downloads a file via SSH (SCP) @@ -362,18 +288,14 @@ func (client *SSHClient) Run(command string, stdout io.Writer, stderr io.Writer) return session.Run(command) } -// Upload uploads a new file via SSH (SCP) -func (client *SSHClient) Upload(src io.Reader, dst string, mode uint32) error { - fileContent, err := ioutil.ReadAll(src) - if err != nil { - return err - } - +// Upload uploads a new file via SSH (SCP). dst is the destination path for the +// file on the remote machine. size is the number of bytes to be uploaded. mode +// is the permissions the file should have, e.g. 0744. +func (client *SSHClient) Upload(src io.Reader, dst string, size int, mode uint32) error { session, err := client.cryptoClient.NewSession() if err != nil { return err } - defer session.Close() w, err := session.StdinPipe() @@ -393,8 +315,8 @@ func (client *SSHClient) Upload(src io.Reader, dst string, mode uint32) error { defer wg.Done() // Signals to the SSH receiver that content is being passed. - fmt.Fprintf(w, "C%#o %d %s\n", mode, len(fileContent), remoteFileName) - _, err = io.Copy(w, bytes.NewReader(fileContent)) + fmt.Fprintf(w, "C%#o %d %s\n", mode, size, remoteFileName) + _, err = io.Copy(w, src) if err != nil { errorChan <- err return @@ -486,3 +408,44 @@ func (client *SSHClient) GetSSHPassword() string { defer client.Creds.mu.Unlock() return client.Creds.SSHPassword } + +// dial will attempt to connect to an SSH server. +var dial = func(network, addr string, config *cssh.ClientConfig) (*cssh.Client, error) { + d := net.Dialer{Timeout: Timeout, KeepAlive: 2 * time.Second} + + conn, err := d.Dial(network, addr) + if err != nil { + return nil, err + } + + c, chans, reqs, err := cssh.NewClientConn(conn, addr, config) + if err != nil { + return nil, err + } + + return cssh.NewClient(c, chans, reqs), nil +} + +var readPrivateKey = func(key string) (cssh.AuthMethod, error) { + signer, err := cssh.ParsePrivateKey([]byte(key)) + if err != nil { + return nil, err + } + + return cssh.PublicKeys(signer), nil +} + +var getAuth = func(c *Credentials, authType string) (cssh.AuthMethod, error) { + var ( + auth cssh.AuthMethod + err error + ) + + switch authType { + case PasswordAuth: + return cssh.Password(c.SSHPassword), nil + case KeyAuth: + return readPrivateKey(c.SSHPrivateKey) + } + return auth, err +} diff --git a/vendor/github.com/vmware/govmomi/event/processor.go b/vendor/github.com/vmware/govmomi/event/processor.go index 1f287b8d..18084d48 100644 --- a/vendor/github.com/vmware/govmomi/event/processor.go +++ b/vendor/github.com/vmware/govmomi/event/processor.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2016 VMware, Inc. All Rights Reserved. +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. @@ -25,8 +25,6 @@ import ( "github.com/vmware/govmomi/vim25/types" ) -const latestPageProp = "latestPage" - type tailInfo struct { t *eventTailer obj types.ManagedObjectReference @@ -81,53 +79,47 @@ func (p *eventProcessor) run(ctx context.Context, tail bool) error { return nil } - var err error var collectors []types.ManagedObjectReference for _, t := range p.tailers { collectors = append(collectors, t.collector) } - if len(p.tailers) > 1 { - // create and populate a ListView - viewMgr := view.NewManager(p.mgr.Client()) - var listView *view.ListView - listView, err = viewMgr.CreateListView(ctx, collectors) - if err != nil { - return err - } + c := property.DefaultCollector(p.mgr.Client()) + props := []string{"latestPage"} - count := 0 - // Retrieve the property from the objects in the ListView - err = property.WaitForView(ctx, property.DefaultCollector(p.mgr.Client()), listView.Reference(), collectors[0], []string{latestPageProp}, func(c types.ManagedObjectReference, pc []types.PropertyChange) bool { - if err = p.process(c, pc); err != nil { + if len(collectors) == 1 { + // only one object to follow, don't bother creating a view + return property.Wait(ctx, c, collectors[0], props, func(pc []types.PropertyChange) bool { + if err := p.process(collectors[0], pc); err != nil { return false } - count++ - if count == len(collectors) && !tail { - return true - } - - return false + return !tail }) + } + // create and populate a ListView + m := view.NewManager(p.mgr.Client()) + + list, err := m.CreateListView(ctx, collectors) + if err != nil { return err } - // only one object to follow - err = property.Wait(ctx, property.DefaultCollector(p.mgr.Client()), collectors[0], []string{latestPageProp}, func(pc []types.PropertyChange) bool { - if err = p.process(collectors[0], pc); err != nil { - return false - } + defer list.Destroy(context.Background()) + + ref := list.Reference() + filter := new(property.WaitFilter).Add(ref, collectors[0].Type, props, list.TraversalSpec()) - if !tail { - return true + return property.WaitForUpdates(ctx, c, filter, func(updates []types.ObjectUpdate) bool { + for _, update := range updates { + if err := p.process(update.Obj, update.ChangeSet); err != nil { + return false + } } - return false + return !tail }) - - return err } func (p *eventProcessor) process(c types.ManagedObjectReference, pc []types.PropertyChange) error { @@ -137,10 +129,6 @@ func (p *eventProcessor) process(c types.ManagedObjectReference, pc []types.Prop } for _, u := range pc { - if u.Name != latestPageProp { - continue - } - evs := t.t.newEvents(u.Val.(types.ArrayOfEvent).Event) if len(evs) == 0 { continue diff --git a/vendor/github.com/vmware/govmomi/examples/datastores/main.go b/vendor/github.com/vmware/govmomi/examples/datastores/main.go index 87902e87..67c8720c 100644 --- a/vendor/github.com/vmware/govmomi/examples/datastores/main.go +++ b/vendor/github.com/vmware/govmomi/examples/datastores/main.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2015 VMware, Inc. All Rights Reserved. +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. @@ -15,7 +15,7 @@ limitations under the License. */ /* -This example program shows how the `finder` and `property` packages can +This example program shows how the `view` and `property` packages can be used to navigate a vSphere inventory structure using govmomi. */ @@ -23,158 +23,59 @@ package main import ( "context" - "flag" "fmt" - "net/url" + "log" "os" - "strings" "text/tabwriter" - "github.com/vmware/govmomi" - "github.com/vmware/govmomi/find" - "github.com/vmware/govmomi/property" + "github.com/vmware/govmomi/examples" "github.com/vmware/govmomi/units" + "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" - "github.com/vmware/govmomi/vim25/types" ) -// GetEnvString returns string from environment variable. -func GetEnvString(v string, def string) string { - r := os.Getenv(v) - if r == "" { - return def - } - - return r -} - -// GetEnvBool returns boolean from environment variable. -func GetEnvBool(v string, def bool) bool { - r := os.Getenv(v) - if r == "" { - return def - } - - switch strings.ToLower(r[0:1]) { - case "t", "y", "1": - return true - } - - return false -} - -const ( - envURL = "GOVMOMI_URL" - envUserName = "GOVMOMI_USERNAME" - envPassword = "GOVMOMI_PASSWORD" - envInsecure = "GOVMOMI_INSECURE" -) - -var urlDescription = fmt.Sprintf("ESX or vCenter URL [%s]", envURL) -var urlFlag = flag.String("url", GetEnvString(envURL, "https://username:password@host/sdk"), urlDescription) - -var insecureDescription = fmt.Sprintf("Don't verify the server's certificate chain [%s]", envInsecure) -var insecureFlag = flag.Bool("insecure", GetEnvBool(envInsecure, false), insecureDescription) - -func processOverride(u *url.URL) { - envUsername := os.Getenv(envUserName) - envPassword := os.Getenv(envPassword) - - // Override username if provided - if envUsername != "" { - var password string - var ok bool - - if u.User != nil { - password, ok = u.User.Password() - } - - if ok { - u.User = url.UserPassword(envUsername, password) - } else { - u.User = url.User(envUsername) - } - } - - // Override password if provided - if envPassword != "" { - var username string - - if u.User != nil { - username = u.User.Username() - } - - u.User = url.UserPassword(username, envPassword) - } -} - -func exit(err error) { - fmt.Fprintf(os.Stderr, "Error: %s\n", err) - os.Exit(1) -} - func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - flag.Parse() - - // Parse URL from string - u, err := url.Parse(*urlFlag) - if err != nil { - exit(err) - } - - // Override username and/or password as required - processOverride(u) - // Connect and log in to ESX or vCenter - c, err := govmomi.NewClient(ctx, u, *insecureFlag) + c, err := examples.NewClient(ctx) if err != nil { - exit(err) + log.Fatal(err) } - f := find.NewFinder(c.Client, true) + defer c.Logout(ctx) - // Find one and only datacenter - dc, err := f.DefaultDatacenter(ctx) - if err != nil { - exit(err) - } - - // Make future calls local to this datacenter - f.SetDatacenter(dc) + // Create a view of Datastore objects + m := view.NewManager(c.Client) - // Find datastores in datacenter - dss, err := f.DatastoreList(ctx, "*") + v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"Datastore"}, true) if err != nil { - exit(err) + log.Fatal(err) } - pc := property.DefaultCollector(c.Client) - - // Convert datastores into list of references - var refs []types.ManagedObjectReference - for _, ds := range dss { - refs = append(refs, ds.Reference()) - } + defer v.Destroy(ctx) // Retrieve summary property for all datastores - var dst []mo.Datastore - err = pc.Retrieve(ctx, refs, []string{"summary"}, &dst) + // Reference: http://pubs.vmware.com/vsphere-60/topic/com.vmware.wssdk.apiref.doc/vim.Datastore.html + var dss []mo.Datastore + err = v.Retrieve(ctx, []string{"Datastore"}, []string{"summary"}, &dss) if err != nil { - exit(err) + log.Fatal(err) } - // Print summary per datastore + // Print summary per datastore (see also: govc/datastore/info.go) + tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0) fmt.Fprintf(tw, "Name:\tType:\tCapacity:\tFree:\n") - for _, ds := range dst { + + for _, ds := range dss { fmt.Fprintf(tw, "%s\t", ds.Summary.Name) fmt.Fprintf(tw, "%s\t", ds.Summary.Type) fmt.Fprintf(tw, "%s\t", units.ByteSize(ds.Summary.Capacity)) fmt.Fprintf(tw, "%s\t", units.ByteSize(ds.Summary.FreeSpace)) fmt.Fprintf(tw, "\n") } - tw.Flush() + + _ = tw.Flush() } diff --git a/vendor/github.com/vmware/govmomi/examples/examples.go b/vendor/github.com/vmware/govmomi/examples/examples.go new file mode 100644 index 00000000..1bd0728f --- /dev/null +++ b/vendor/github.com/vmware/govmomi/examples/examples.go @@ -0,0 +1,116 @@ +/* +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 examples + +import ( + "context" + "flag" + "fmt" + "net/url" + "os" + "strings" + + "github.com/vmware/govmomi" + "github.com/vmware/govmomi/vim25/soap" +) + +// getEnvString returns string from environment variable. +func getEnvString(v string, def string) string { + r := os.Getenv(v) + if r == "" { + return def + } + + return r +} + +// getEnvBool returns boolean from environment variable. +func getEnvBool(v string, def bool) bool { + r := os.Getenv(v) + if r == "" { + return def + } + + switch strings.ToLower(r[0:1]) { + case "t", "y", "1": + return true + } + + return false +} + +const ( + envURL = "GOVMOMI_URL" + envUserName = "GOVMOMI_USERNAME" + envPassword = "GOVMOMI_PASSWORD" + envInsecure = "GOVMOMI_INSECURE" +) + +var urlDescription = fmt.Sprintf("ESX or vCenter URL [%s]", envURL) +var urlFlag = flag.String("url", getEnvString(envURL, "https://username:password@host/sdk"), urlDescription) + +var insecureDescription = fmt.Sprintf("Don't verify the server's certificate chain [%s]", envInsecure) +var insecureFlag = flag.Bool("insecure", getEnvBool(envInsecure, false), insecureDescription) + +func processOverride(u *url.URL) { + envUsername := os.Getenv(envUserName) + envPassword := os.Getenv(envPassword) + + // Override username if provided + if envUsername != "" { + var password string + var ok bool + + if u.User != nil { + password, ok = u.User.Password() + } + + if ok { + u.User = url.UserPassword(envUsername, password) + } else { + u.User = url.User(envUsername) + } + } + + // Override password if provided + if envPassword != "" { + var username string + + if u.User != nil { + username = u.User.Username() + } + + u.User = url.UserPassword(username, envPassword) + } +} + +// NewClient creates a govmomi.Client for use in the examples +func NewClient(ctx context.Context) (*govmomi.Client, error) { + flag.Parse() + + // Parse URL from string + u, err := soap.ParseURL(*urlFlag) + if err != nil { + return nil, err + } + + // Override username and/or password as required + processOverride(u) + + // Connect and log in to ESX or vCenter + return govmomi.NewClient(ctx, u, *insecureFlag) +} diff --git a/vendor/github.com/vmware/govmomi/examples/hosts/main.go b/vendor/github.com/vmware/govmomi/examples/hosts/main.go new file mode 100644 index 00000000..625e2aed --- /dev/null +++ b/vendor/github.com/vmware/govmomi/examples/hosts/main.go @@ -0,0 +1,87 @@ +/* +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. +*/ + +/* +This example program shows how the `view` and `property` packages can +be used to navigate a vSphere inventory structure using govmomi. +*/ + +package main + +import ( + "context" + "fmt" + "log" + "os" + "text/tabwriter" + + "github.com/vmware/govmomi/examples" + "github.com/vmware/govmomi/units" + "github.com/vmware/govmomi/view" + "github.com/vmware/govmomi/vim25/mo" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Connect and login to ESX or vCenter + c, err := examples.NewClient(ctx) + if err != nil { + log.Fatal(err) + } + + defer c.Logout(ctx) + + // Create a view of HostSystem objects + m := view.NewManager(c.Client) + + v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"HostSystem"}, true) + if err != nil { + log.Fatal(err) + } + + defer v.Destroy(ctx) + + // Retrieve summary property for all hosts + // Reference: http://pubs.vmware.com/vsphere-60/topic/com.vmware.wssdk.apiref.doc/vim.HostSystem.html + var hss []mo.HostSystem + err = v.Retrieve(ctx, []string{"HostSystem"}, []string{"summary"}, &hss) + if err != nil { + log.Fatal(err) + } + + // Print summary per host (see also: govc/host/info.go) + + tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0) + fmt.Fprintf(tw, "Name:\tUsed CPU:\tTotal CPU:\tFree CPU:\tUsed Memory:\tTotal Memory:\tFree Memory\t:\n") + + for _, hs := range hss { + totalCPU := int64(hs.Summary.Hardware.CpuMhz) * int64(hs.Summary.Hardware.NumCpuCores) + freeCPU := int64(totalCPU) - int64(hs.Summary.QuickStats.OverallCpuUsage) + freeMemory := int64(hs.Summary.Hardware.MemorySize) - (int64(hs.Summary.QuickStats.OverallMemoryUsage) * 1024 * 1024) + fmt.Fprintf(tw, "%s\t", hs.Summary.Config.Name) + fmt.Fprintf(tw, "%d\t", hs.Summary.QuickStats.OverallCpuUsage) + fmt.Fprintf(tw, "%d\t", totalCPU) + fmt.Fprintf(tw, "%d\t", freeCPU) + fmt.Fprintf(tw, "%s\t", units.ByteSize(hs.Summary.QuickStats.OverallMemoryUsage)) + fmt.Fprintf(tw, "%s\t", units.ByteSize(hs.Summary.Hardware.MemorySize)) + fmt.Fprintf(tw, "%d\t", freeMemory) + fmt.Fprintf(tw, "\n") + } + + _ = tw.Flush() +} diff --git a/vendor/github.com/vmware/govmomi/examples/networks/main.go b/vendor/github.com/vmware/govmomi/examples/networks/main.go new file mode 100644 index 00000000..1640af34 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/examples/networks/main.go @@ -0,0 +1,61 @@ +/* +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 main + +import ( + "context" + "fmt" + "log" + + "github.com/vmware/govmomi/examples" + "github.com/vmware/govmomi/view" + "github.com/vmware/govmomi/vim25/mo" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Connect and login to ESX or vCenter + c, err := examples.NewClient(ctx) + if err != nil { + log.Fatal(err) + } + + defer c.Logout(ctx) + + // Create a view of Network types + m := view.NewManager(c.Client) + + v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"Network"}, true) + if err != nil { + log.Fatal(err) + } + + defer v.Destroy(ctx) + + // Reference: http://pubs.vmware.com/vsphere-60/topic/com.vmware.wssdk.apiref.doc/vim.Network.html + var networks []mo.Network + err = v.Retrieve(ctx, []string{"Network"}, nil, &networks) + if err != nil { + log.Fatal(err) + } + + for _, net := range networks { + fmt.Printf("%s: %s\n", net.Name, net.Reference()) + } +} diff --git a/vendor/github.com/vmware/govmomi/examples/virtualmachines/main.go b/vendor/github.com/vmware/govmomi/examples/virtualmachines/main.go new file mode 100644 index 00000000..f6982e41 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/examples/virtualmachines/main.go @@ -0,0 +1,69 @@ +/* +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. +*/ + +/* +This example program shows how the `view` package can +be used to navigate a vSphere inventory structure using govmomi. +*/ + +package main + +import ( + "context" + "fmt" + "log" + + "github.com/vmware/govmomi/examples" + "github.com/vmware/govmomi/view" + "github.com/vmware/govmomi/vim25/mo" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Connect and login to ESX or vCenter + c, err := examples.NewClient(ctx) + if err != nil { + log.Fatal(err) + } + + defer c.Logout(ctx) + + // Create view of VirtualMachine objects + m := view.NewManager(c.Client) + + v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"VirtualMachine"}, true) + if err != nil { + log.Fatal(err) + } + + defer v.Destroy(ctx) + + // Retrieve summary property for all machines + // Reference: http://pubs.vmware.com/vsphere-60/topic/com.vmware.wssdk.apiref.doc/vim.VirtualMachine.html + var vms []mo.VirtualMachine + err = v.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary"}, &vms) + if err != nil { + log.Fatal(err) + } + + // Print summary per vm (see also: govc/vm/info.go) + + for _, vm := range vms { + fmt.Printf("%s: %s\n", vm.Summary.Config.Name, vm.Summary.Config.GuestFullName) + } +} diff --git a/vendor/github.com/vmware/govmomi/find/doc.go b/vendor/github.com/vmware/govmomi/find/doc.go new file mode 100644 index 00000000..0c8acee0 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/find/doc.go @@ -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 diff --git a/vendor/github.com/vmware/govmomi/find/finder.go b/vendor/github.com/vmware/govmomi/find/finder.go index 9f638a52..04d0e891 100644 --- a/vendor/github.com/vmware/govmomi/find/finder.go +++ b/vendor/github.com/vmware/govmomi/find/finder.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved. +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. @@ -20,6 +20,7 @@ import ( "context" "errors" "path" + "strings" "github.com/vmware/govmomi/list" "github.com/vmware/govmomi/object" @@ -30,17 +31,18 @@ import ( ) type Finder struct { - client *vim25.Client - recurser list.Recurser - + client *vim25.Client + r recurser dc *object.Datacenter + si *object.SearchIndex folders *object.DatacenterFolders } func NewFinder(client *vim25.Client, all bool) *Finder { f := &Finder{ client: client, - recurser: list.Recurser{ + si: object.NewSearchIndex(client), + r: recurser{ Collector: property.DefaultCollector(client), All: all, }, @@ -55,9 +57,41 @@ func (f *Finder) SetDatacenter(dc *object.Datacenter) *Finder { return f } -type findRelativeFunc func(ctx context.Context) (object.Reference, error) +// findRoot makes it possible to use "find" mode with a different root path. +// Example: ResourcePoolList("/dc1/host/cluster1/...") +func (f *Finder) findRoot(ctx context.Context, root *list.Element, parts []string) bool { + if len(parts) == 0 { + return false + } + + ix := len(parts) - 1 + + if parts[ix] != "..." { + return false + } + + if ix == 0 { + return true // We already have the Object for root.Path + } + + // Lookup the Object for the new root.Path + rootPath := path.Join(root.Path, path.Join(parts[:ix]...)) + + ref, err := f.si.FindByInventoryPath(ctx, rootPath) + if err != nil || ref == nil { + // If we get an error or fail to match, fall through to find() with the original root and path + return false + } + + root.Path = rootPath + root.Object = ref + + return true +} + +func (f *Finder) find(ctx context.Context, arg string, s *spec) ([]list.Element, error) { + isPath := strings.Contains(arg, "/") -func (f *Finder) find(ctx context.Context, fn findRelativeFunc, tl bool, arg string) ([]list.Element, error) { root := list.Element{ Path: "/", Object: object.NewRootFolder(f.client), @@ -70,7 +104,7 @@ func (f *Finder) find(ctx context.Context, fn findRelativeFunc, tl bool, arg str case "..": // Not supported; many edge case, little value return nil, errors.New("cannot traverse up a tree") case ".": // Relative to whatever - pivot, err := fn(ctx) + pivot, err := s.Relative(ctx) if err != nil { return nil, err } @@ -93,13 +127,17 @@ func (f *Finder) find(ctx context.Context, fn findRelativeFunc, tl bool, arg str } } - f.recurser.TraverseLeafs = tl - es, err := f.recurser.Recurse(ctx, root, parts) - if err != nil { - return nil, err + if s.listMode(isPath) { + if f.findRoot(ctx, &root, parts) { + parts = []string{"*"} + } else { + return f.r.List(ctx, s, root, parts) + } } - return es, nil + s.Parents = append(s.Parents, s.Nested...) + + return f.r.Find(ctx, s, root, parts) } func (f *Finder) datacenter() (*object.Datacenter, error) { @@ -208,7 +246,7 @@ func (f *Finder) rootFolder(_ context.Context) (object.Reference, error) { return object.NewRootFolder(f.client), nil } -func (f *Finder) managedObjectList(ctx context.Context, path string, tl bool) ([]list.Element, error) { +func (f *Finder) managedObjectList(ctx context.Context, path string, tl bool, include []string) ([]list.Element, error) { fn := f.rootFolder if f.dc != nil { @@ -219,7 +257,18 @@ func (f *Finder) managedObjectList(ctx context.Context, path string, tl bool) ([ path = "." } - return f.find(ctx, fn, tl, path) + s := &spec{ + Relative: fn, + Parents: []string{"ComputeResource", "ClusterComputeResource", "HostSystem", "VirtualApp", "StoragePod"}, + Include: include, + } + + if tl { + s.Contents = true + s.ListMode = types.NewBool(true) + } + + return f.find(ctx, path, s) } // Element returns an Element for the given ManagedObjectReference @@ -229,7 +278,11 @@ func (f *Finder) Element(ctx context.Context, ref types.ManagedObjectReference) return ref, nil } - e, err := f.find(ctx, rl, false, ".") + s := &spec{ + Relative: rl, + } + + e, err := f.find(ctx, "./", s) if err != nil { return nil, err } @@ -270,16 +323,21 @@ func (f *Finder) ObjectReference(ctx context.Context, ref types.ManagedObjectRef return r, nil } -func (f *Finder) ManagedObjectList(ctx context.Context, path string) ([]list.Element, error) { - return f.managedObjectList(ctx, path, false) +func (f *Finder) ManagedObjectList(ctx context.Context, path string, include ...string) ([]list.Element, error) { + return f.managedObjectList(ctx, path, false, include) } -func (f *Finder) ManagedObjectListChildren(ctx context.Context, path string) ([]list.Element, error) { - return f.managedObjectList(ctx, path, true) +func (f *Finder) ManagedObjectListChildren(ctx context.Context, path string, include ...string) ([]list.Element, error) { + return f.managedObjectList(ctx, path, true, include) } func (f *Finder) DatacenterList(ctx context.Context, path string) ([]*object.Datacenter, error) { - es, err := f.find(ctx, f.rootFolder, false, path) + s := &spec{ + Relative: f.rootFolder, + Include: []string{"Datacenter"}, + } + + es, err := f.find(ctx, path, s) if err != nil { return nil, err } @@ -336,7 +394,12 @@ func (f *Finder) DatacenterOrDefault(ctx context.Context, path string) (*object. } func (f *Finder) DatastoreList(ctx context.Context, path string) ([]*object.Datastore, error) { - es, err := f.find(ctx, f.datastoreFolder, false, path) + s := &spec{ + Relative: f.datastoreFolder, + Parents: []string{"StoragePod"}, + } + + es, err := f.find(ctx, path, s) if err != nil { return nil, err } @@ -404,7 +467,11 @@ func (f *Finder) DatastoreOrDefault(ctx context.Context, path string) (*object.D } func (f *Finder) DatastoreClusterList(ctx context.Context, path string) ([]*object.StoragePod, error) { - es, err := f.find(ctx, f.datastoreFolder, false, path) + s := &spec{ + Relative: f.datastoreFolder, + } + + es, err := f.find(ctx, path, s) if err != nil { return nil, err } @@ -461,7 +528,11 @@ func (f *Finder) DatastoreClusterOrDefault(ctx context.Context, path string) (*o } func (f *Finder) ComputeResourceList(ctx context.Context, path string) ([]*object.ComputeResource, error) { - es, err := f.find(ctx, f.hostFolder, false, path) + s := &spec{ + Relative: f.hostFolder, + } + + es, err := f.find(ctx, path, s) if err != nil { return nil, err } @@ -523,7 +594,11 @@ func (f *Finder) ComputeResourceOrDefault(ctx context.Context, path string) (*ob } func (f *Finder) ClusterComputeResourceList(ctx context.Context, path string) ([]*object.ClusterComputeResource, error) { - es, err := f.find(ctx, f.hostFolder, false, path) + s := &spec{ + Relative: f.hostFolder, + } + + es, err := f.find(ctx, path, s) if err != nil { return nil, err } @@ -564,7 +639,13 @@ func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*obje } func (f *Finder) HostSystemList(ctx context.Context, path string) ([]*object.HostSystem, error) { - es, err := f.find(ctx, f.hostFolder, false, path) + s := &spec{ + Relative: f.hostFolder, + Parents: []string{"ComputeResource", "ClusterComputeResource"}, + Include: []string{"HostSystem"}, + } + + es, err := f.find(ctx, path, s) if err != nil { return nil, err } @@ -635,7 +716,11 @@ func (f *Finder) HostSystemOrDefault(ctx context.Context, path string) (*object. } func (f *Finder) NetworkList(ctx context.Context, path string) ([]object.NetworkReference, error) { - es, err := f.find(ctx, f.networkFolder, false, path) + s := &spec{ + Relative: f.networkFolder, + } + + es, err := f.find(ctx, path, s) if err != nil { return nil, err } @@ -644,10 +729,14 @@ func (f *Finder) NetworkList(ctx context.Context, path string) ([]object.Network for _, e := range es { ref := e.Object.Reference() switch ref.Type { - case "Network", "OpaqueNetwork": + case "Network": r := object.NewNetwork(f.client, ref) r.InventoryPath = e.Path ns = append(ns, r) + case "OpaqueNetwork": + r := object.NewOpaqueNetwork(f.client, ref) + r.InventoryPath = e.Path + ns = append(ns, r) case "DistributedVirtualPortgroup": r := object.NewDistributedVirtualPortgroup(f.client, ref) r.InventoryPath = e.Path @@ -701,7 +790,14 @@ func (f *Finder) NetworkOrDefault(ctx context.Context, path string) (object.Netw } func (f *Finder) ResourcePoolList(ctx context.Context, path string) ([]*object.ResourcePool, error) { - es, err := f.find(ctx, f.hostFolder, true, path) + s := &spec{ + Relative: f.hostFolder, + Parents: []string{"ComputeResource", "ClusterComputeResource", "VirtualApp"}, + Nested: []string{"ResourcePool"}, + Contents: true, + } + + es, err := f.find(ctx, path, s) if err != nil { return nil, err } @@ -804,7 +900,12 @@ func (f *Finder) FolderOrDefault(ctx context.Context, path string) (*object.Fold } func (f *Finder) VirtualMachineList(ctx context.Context, path string) ([]*object.VirtualMachine, error) { - es, err := f.find(ctx, f.vmFolder, false, path) + s := &spec{ + Relative: f.vmFolder, + Parents: []string{"VirtualApp"}, + } + + es, err := f.find(ctx, path, s) if err != nil { return nil, err } @@ -840,7 +941,11 @@ func (f *Finder) VirtualMachine(ctx context.Context, path string) (*object.Virtu } func (f *Finder) VirtualAppList(ctx context.Context, path string) ([]*object.VirtualApp, error) { - es, err := f.find(ctx, f.vmFolder, false, path) + s := &spec{ + Relative: f.vmFolder, + } + + es, err := f.find(ctx, path, s) if err != nil { return nil, err } diff --git a/vendor/github.com/vmware/govmomi/find/recurser.go b/vendor/github.com/vmware/govmomi/find/recurser.go new file mode 100644 index 00000000..80602956 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/find/recurser.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/govc/datacenter/info.go b/vendor/github.com/vmware/govmomi/govc/datacenter/info.go index 1850965a..e0fd0d0c 100644 --- a/vendor/github.com/vmware/govmomi/govc/datacenter/info.go +++ b/vendor/github.com/vmware/govmomi/govc/datacenter/info.go @@ -148,6 +148,7 @@ func (r *infoResult) Write(w io.Writer) error { for _, o := range r.objects { dc := objects[o.Reference()] fmt.Fprintf(tw, "Name:\t%s\n", dc.Name) + fmt.Fprintf(tw, " Path:\t%s\n", o.InventoryPath) folders, err := o.Folders(r.ctx) if err != nil { diff --git a/vendor/github.com/vmware/govmomi/govc/datastore/cp.go b/vendor/github.com/vmware/govmomi/govc/datastore/cp.go index 44925c54..83c49553 100644 --- a/vendor/github.com/vmware/govmomi/govc/datastore/cp.go +++ b/vendor/github.com/vmware/govmomi/govc/datastore/cp.go @@ -61,6 +61,14 @@ func (cmd *cp) Usage() string { return "SRC DST" } +func (cmd *cp) Description() string { + return `Copy SRC to DST on DATASTORE. + +Examples: + govc datastore.cp foo/foo.vmx foo/foo.vmx.old + govc datastore.cp -f my.vmx foo/foo.vmx` +} + func (cmd *cp) Run(ctx context.Context, f *flag.FlagSet) error { args := f.Args() if len(args) != 2 { diff --git a/vendor/github.com/vmware/govmomi/govc/datastore/disk/create.go b/vendor/github.com/vmware/govmomi/govc/datastore/disk/create.go new file mode 100644 index 00000000..c629c2a2 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/datastore/disk/create.go @@ -0,0 +1,98 @@ +/* +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 disk + +import ( + "context" + "flag" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/units" + "github.com/vmware/govmomi/vim25/types" +) + +type create struct { + *flags.DatastoreFlag + + Bytes units.ByteSize +} + +func init() { + cli.Register("datastore.disk.create", &create{}) +} + +func (cmd *create) Register(ctx context.Context, f *flag.FlagSet) { + cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx) + cmd.DatastoreFlag.Register(ctx, f) + + _ = cmd.Bytes.Set("10G") + f.Var(&cmd.Bytes, "size", "Size of new disk") +} + +func (cmd *create) Process(ctx context.Context) error { + if err := cmd.DatastoreFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *create) Usage() string { + return "VMDK" +} + +func (cmd *create) Description() string { + return `Create VMDK on DS. + +Examples: + govc datastore.mkdir disks + govc datastore.disk.create -size 24G disks/disk1.vmdk` +} + +func (cmd *create) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() == 0 { + return flag.ErrHelp + } + + dc, err := cmd.Datacenter() + if err != nil { + return err + } + + ds, err := cmd.Datastore() + if err != nil { + return err + } + + m := object.NewVirtualDiskManager(ds.Client()) + + spec := &types.FileBackedVirtualDiskSpec{ + VirtualDiskSpec: types.VirtualDiskSpec{ + AdapterType: string(types.VirtualDiskAdapterTypeLsiLogic), + DiskType: string(types.VirtualDiskTypeThin), + }, + CapacityKb: int64(cmd.Bytes) / 1024, + } + + task, err := m.CreateVirtualDisk(ctx, ds.Path(f.Arg(0)), dc, spec) + if err != nil { + return err + } + + return task.Wait(ctx) +} diff --git a/vendor/github.com/vmware/govmomi/govc/datastore/disk/info.go b/vendor/github.com/vmware/govmomi/govc/datastore/disk/info.go new file mode 100644 index 00000000..338c5c82 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/datastore/disk/info.go @@ -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 disk + +import ( + "context" + "flag" + "fmt" + "io" + "strings" + "text/tabwriter" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/object" +) + +type info struct { + *flags.DatastoreFlag + + c bool + d bool + p bool +} + +func init() { + cli.Register("datastore.disk.info", &info{}) +} + +func (cmd *info) Register(ctx context.Context, f *flag.FlagSet) { + cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx) + cmd.DatastoreFlag.Register(ctx, f) + + f.BoolVar(&cmd.c, "c", false, "Chain format") + f.BoolVar(&cmd.d, "d", false, "Include datastore in output") + f.BoolVar(&cmd.p, "p", true, "Include parents") +} + +func (cmd *info) Process(ctx context.Context) error { + if err := cmd.DatastoreFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *info) Usage() string { + return "VMDK" +} + +func (cmd *info) Description() string { + return `Query VMDK info on DS. + +Examples: + govc datastore.disk.info disks/disk1.vmdk` +} + +func fullPath(s string) string { + return s +} + +func dsPath(s string) string { + var p object.DatastorePath + + if p.FromString(s) { + return p.Path + } + + return s +} + +var infoPath = dsPath + +type infoResult []object.VirtualDiskInfo + +func (r infoResult) Write(w io.Writer) error { + tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0) + + for _, info := range r { + fmt.Fprintf(tw, "Name:\t%s\n", infoPath(info.Name)) + fmt.Fprintf(tw, " Type:\t%s\n", info.DiskType) + fmt.Fprintf(tw, " Parent:\t%s\n", infoPath(info.Parent)) + } + + return tw.Flush() +} + +type chainResult []object.VirtualDiskInfo + +func (r chainResult) Write(w io.Writer) error { + for i, info := range r { + fmt.Fprint(w, strings.Repeat(" ", i*2)) + fmt.Fprintln(w, infoPath(info.Name)) + } + + return nil +} + +func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() != 1 { + return flag.ErrHelp + } + + dc, err := cmd.Datacenter() + if err != nil { + return err + } + + ds, err := cmd.Datastore() + if err != nil { + return err + } + + m := object.NewVirtualDiskManager(ds.Client()) + + info, err := m.QueryVirtualDiskInfo(ctx, ds.Path(f.Arg(0)), dc, cmd.p) + if err != nil { + return err + } + + if cmd.d { + infoPath = fullPath + } + + var r flags.OutputWriter = infoResult(info) + + if cmd.c { + r = chainResult(info) + } + + return cmd.WriteResult(r) +} diff --git a/vendor/github.com/vmware/govmomi/govc/datastore/ls.go b/vendor/github.com/vmware/govmomi/govc/datastore/ls.go index f0486c9c..6c33a27f 100644 --- a/vendor/github.com/vmware/govmomi/govc/datastore/ls.go +++ b/vendor/github.com/vmware/govmomi/govc/datastore/ls.go @@ -74,6 +74,17 @@ func (cmd *ls) Usage() string { return "[FILE]..." } +func isInvalid(err error) bool { + if f, ok := err.(types.HasFault); ok { + switch f.Fault().(type) { + case *types.InvalidArgument: + return true + } + } + + return false +} + func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { ds, err := cmd.Datastore() if err != nil { @@ -113,7 +124,7 @@ func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { r, err := cmd.ListPath(b, arg, spec) if err != nil { // Treat the argument as a match pattern if not found as directory - if i == 0 && types.IsFileNotFound(err) { + if i == 0 && types.IsFileNotFound(err) || isInvalid(err) { spec.MatchPattern[0] = path.Base(arg) arg = path.Dir(arg) continue @@ -187,7 +198,7 @@ func (o *listOutput) add(r types.HostDatastoreBrowserSearchResults) { } for _, p := range path { - if p[0] == '.' { + if len(p) != 0 && p[0] == '.' { return } } diff --git a/vendor/github.com/vmware/govmomi/govc/datastore/mv.go b/vendor/github.com/vmware/govmomi/govc/datastore/mv.go index 7c99550e..ee11c376 100644 --- a/vendor/github.com/vmware/govmomi/govc/datastore/mv.go +++ b/vendor/github.com/vmware/govmomi/govc/datastore/mv.go @@ -54,6 +54,14 @@ func (cmd *mv) Usage() string { return "SRC DST" } +func (cmd *mv) Description() string { + return `Move SRC to DST on DATASTORE. + +Examples: + govc datastore.mv foo/foo.vmx foo/foo.vmx.old + govc datastore.mv -f my.vmx foo/foo.vmx` +} + func (cmd *mv) Run(ctx context.Context, f *flag.FlagSet) error { args := f.Args() if len(args) != 2 { diff --git a/vendor/github.com/vmware/govmomi/govc/datastore/rm.go b/vendor/github.com/vmware/govmomi/govc/datastore/rm.go index 58676ba5..9c292024 100644 --- a/vendor/github.com/vmware/govmomi/govc/datastore/rm.go +++ b/vendor/github.com/vmware/govmomi/govc/datastore/rm.go @@ -18,7 +18,6 @@ package datastore import ( "context" - "errors" "flag" "github.com/vmware/govmomi/govc/cli" @@ -30,6 +29,7 @@ import ( type rm struct { *flags.DatastoreFlag + kind bool force bool isNamespace bool } @@ -43,6 +43,7 @@ func (cmd *rm) Register(ctx context.Context, f *flag.FlagSet) { cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx) cmd.DatastoreFlag.Register(ctx, f) + f.BoolVar(&cmd.kind, "t", true, "Use file type to choose disk or file manager") f.BoolVar(&cmd.force, "f", false, "Force; ignore nonexistent files and arguments") f.BoolVar(&cmd.isNamespace, "namespace", false, "Path is uuid of namespace on vsan datastore") } @@ -58,10 +59,19 @@ func (cmd *rm) Usage() string { return "FILE" } +func (cmd *rm) Description() string { + return `Remove FILE from DATASTORE. + +Examples: + govc datastore.rm vm/vmware.log + govc datastore.rm vm + govc datastore.rm -f images/base.vmdk` +} + func (cmd *rm) Run(ctx context.Context, f *flag.FlagSet) error { args := f.Args() if len(args) == 0 { - return errors.New("missing operand") + return flag.ErrHelp } c, err := cmd.Client() @@ -75,28 +85,25 @@ func (cmd *rm) Run(ctx context.Context, f *flag.FlagSet) error { return err } + ds, err := cmd.Datastore() + if err != nil { + return err + } + if cmd.isNamespace { path := args[0] nm := object.NewDatastoreNamespaceManager(c) err = nm.DeleteDirectory(ctx, dc, path) } else { - var path string - var task *object.Task - - // TODO(PN): Accept multiple args - path, err = cmd.DatastorePath(args[0]) - if err != nil { - return err - } + fm := ds.NewFileManager(dc, cmd.force) - m := object.NewFileManager(c) - task, err = m.DeleteDatastoreFile(ctx, path, dc) - if err != nil { - return err + remove := fm.DeleteFile // File delete + if cmd.kind { + remove = fm.Delete // VirtualDisk or File delete } - err = task.Wait(ctx) + err = remove(ctx, args[0]) } if err != nil { diff --git a/vendor/github.com/vmware/govmomi/govc/datastore/vsan/ls.go b/vendor/github.com/vmware/govmomi/govc/datastore/vsan/ls.go new file mode 100644 index 00000000..3b7b50f1 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/datastore/vsan/ls.go @@ -0,0 +1,156 @@ +/* +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 vsan + +import ( + "context" + "flag" + "fmt" + "net/url" + "text/tabwriter" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/mo" +) + +type ls struct { + *flags.DatastoreFlag + + long bool + orphan bool +} + +func init() { + cli.Register("datastore.vsan.dom.ls", &ls{}) +} + +func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) { + cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx) + cmd.DatastoreFlag.Register(ctx, f) + + f.BoolVar(&cmd.long, "l", false, "Long listing") + f.BoolVar(&cmd.orphan, "o", false, "List orphan objects") +} + +func (cmd *ls) Process(ctx context.Context) error { + if err := cmd.DatastoreFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *ls) Usage() string { + return "[UUID]..." +} + +func (cmd *ls) Description() string { + return `List vSAN DOM objects in DS. + +Examples: + govc datastore.vsan.dom.ls + govc datastore.vsan.dom.ls -ds vsanDatastore -l + govc datastore.vsan.dom.ls -l d85aa758-63f5-500a-3150-0200308e589c` +} + +func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { + ds, err := cmd.Datastore() + if err != nil { + return err + } + + var mds mo.Datastore + err = ds.Properties(ctx, ds.Reference(), []string{"summary"}, &mds) + if err != nil { + return err + } + + if mds.Summary.Type != "vsan" { + return flag.ErrHelp + } + + hosts, err := ds.AttachedHosts(ctx) + if err != nil { + return err + } + + if len(hosts) == 0 { + return flag.ErrHelp + } + + m, err := hosts[0].ConfigManager().VsanInternalSystem(ctx) + if err != nil { + return err + } + + ids, err := m.QueryVsanObjectUuidsByFilter(ctx, f.Args(), 0, 0) + if err != nil { + return err + } + + if len(ids) == 0 { + return nil + } + + if !cmd.long && !cmd.orphan { + for _, id := range ids { + fmt.Fprintln(cmd.Out, id) + } + + return nil + } + + objs, err := m.GetVsanObjExtAttrs(ctx, ids) + if err != nil { + return err + } + + u, err := url.Parse(mds.Summary.Url) + if err != nil { + return err + } + + tw := tabwriter.NewWriter(cmd.Out, 2, 0, 2, ' ', 0) + cmd.Out = tw + + for id, obj := range objs { + path := obj.DatastorePath(u.Path) + + if cmd.orphan { + _, err = ds.Stat(ctx, path) + if err == nil { + continue + } + + switch err.(type) { + case object.DatastoreNoSuchDirectoryError, object.DatastoreNoSuchFileError: + default: + return err + } + + if !cmd.long { + fmt.Fprintln(cmd.Out, id) + continue + } + } + + fmt.Fprintf(cmd.Out, "%s\t%s\t%s\n", id, obj.Class, path) + } + + return tw.Flush() +} diff --git a/vendor/github.com/vmware/govmomi/govc/datastore/vsan/rm.go b/vendor/github.com/vmware/govmomi/govc/datastore/vsan/rm.go new file mode 100644 index 00000000..23024716 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/datastore/vsan/rm.go @@ -0,0 +1,108 @@ +/* +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 vsan + +import ( + "context" + "flag" + "fmt" + "os" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" +) + +type rm struct { + *flags.DatastoreFlag + + force bool + verbose bool +} + +func init() { + cli.Register("datastore.vsan.dom.rm", &rm{}) +} + +func (cmd *rm) Register(ctx context.Context, f *flag.FlagSet) { + cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx) + cmd.DatastoreFlag.Register(ctx, f) + + f.BoolVar(&cmd.force, "f", false, "Force delete") + f.BoolVar(&cmd.verbose, "v", false, "Print deleted UUIDs to stdout, failed to stderr") +} + +func (cmd *rm) Process(ctx context.Context) error { + if err := cmd.DatastoreFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *rm) Usage() string { + return "UUID..." +} + +func (cmd *rm) Description() string { + return `Remove vSAN DOM objects in DS. + +Examples: + govc datastore.vsan.dom.rm d85aa758-63f5-500a-3150-0200308e589c + govc datastore.vsan.dom.rm -f d85aa758-63f5-500a-3150-0200308e589c + govc datastore.vsan.dom.ls -o | xargs govc datastore.vsan.dom.rm` +} + +func (cmd *rm) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() == 0 { + return flag.ErrHelp + } + + ds, err := cmd.Datastore() + if err != nil { + return err + } + + hosts, err := ds.AttachedHosts(ctx) + if err != nil { + return err + } + + if len(hosts) == 0 { + return flag.ErrHelp + } + + m, err := hosts[0].ConfigManager().VsanInternalSystem(ctx) + if err != nil { + return err + } + + res, err := m.DeleteVsanObjects(ctx, f.Args(), &cmd.force) + if err != nil { + return err + } + + if cmd.verbose { + for _, r := range res { + if r.Success { + fmt.Fprintln(cmd.Out, r.Uuid) + } else { + fmt.Fprintf(os.Stderr, "%s %s\n", r.Uuid, r.FailureReason[0].Message) + } + } + } + + return nil +} diff --git a/vendor/github.com/vmware/govmomi/govc/device/boot.go b/vendor/github.com/vmware/govmomi/govc/device/boot.go index 970b0594..977b10c0 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/boot.go +++ b/vendor/github.com/vmware/govmomi/govc/device/boot.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/govc/device/cdrom/add.go b/vendor/github.com/vmware/govmomi/govc/device/cdrom/add.go index e42c6b1f..24089a48 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/cdrom/add.go +++ b/vendor/github.com/vmware/govmomi/govc/device/cdrom/add.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/govc/device/cdrom/eject.go b/vendor/github.com/vmware/govmomi/govc/device/cdrom/eject.go index 78cce90d..330ba3d8 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/cdrom/eject.go +++ b/vendor/github.com/vmware/govmomi/govc/device/cdrom/eject.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/govc/device/cdrom/insert.go b/vendor/github.com/vmware/govmomi/govc/device/cdrom/insert.go index 4ff8142c..fef8d996 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/cdrom/insert.go +++ b/vendor/github.com/vmware/govmomi/govc/device/cdrom/insert.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/govc/device/connect.go b/vendor/github.com/vmware/govmomi/govc/device/connect.go index bb6b0661..a0fc57aa 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/connect.go +++ b/vendor/github.com/vmware/govmomi/govc/device/connect.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -49,6 +49,13 @@ func (cmd *connect) Usage() string { return "DEVICE..." } +func (cmd *connect) Description() string { + return `Connect DEVICE on VM. + +Examples: + govc device.connect -vm $name cdrom-3000` +} + func (cmd *connect) Run(ctx context.Context, f *flag.FlagSet) error { vm, err := cmd.VirtualMachine() if err != nil { diff --git a/vendor/github.com/vmware/govmomi/govc/device/disconnect.go b/vendor/github.com/vmware/govmomi/govc/device/disconnect.go index 95a22ca7..503de17f 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/disconnect.go +++ b/vendor/github.com/vmware/govmomi/govc/device/disconnect.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -49,6 +49,13 @@ func (cmd *disconnect) Usage() string { return "DEVICE..." } +func (cmd *disconnect) Description() string { + return `Disconnect DEVICE on VM. + +Examples: + govc device.disconnect -vm $name cdrom-3000` +} + func (cmd *disconnect) Run(ctx context.Context, f *flag.FlagSet) error { vm, err := cmd.VirtualMachine() if err != nil { diff --git a/vendor/github.com/vmware/govmomi/govc/device/floppy/add.go b/vendor/github.com/vmware/govmomi/govc/device/floppy/add.go index 9c3716d0..bcb5512a 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/floppy/add.go +++ b/vendor/github.com/vmware/govmomi/govc/device/floppy/add.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/govc/device/floppy/eject.go b/vendor/github.com/vmware/govmomi/govc/device/floppy/eject.go index 5ef9b08b..37c1aae1 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/floppy/eject.go +++ b/vendor/github.com/vmware/govmomi/govc/device/floppy/eject.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/govc/device/floppy/insert.go b/vendor/github.com/vmware/govmomi/govc/device/floppy/insert.go index f61ea4ff..83f6f804 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/floppy/insert.go +++ b/vendor/github.com/vmware/govmomi/govc/device/floppy/insert.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/govc/device/info.go b/vendor/github.com/vmware/govmomi/govc/device/info.go index a2eae500..6c4a801f 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/info.go +++ b/vendor/github.com/vmware/govmomi/govc/device/info.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -18,11 +18,12 @@ package device import ( "context" + "encoding/json" "flag" "fmt" "io" "os" - "path/filepath" + "path" "strings" "text/tabwriter" @@ -70,10 +71,19 @@ func (cmd *info) Usage() string { return "[DEVICE]..." } +func (cmd *info) Description() string { + return `Device info for VM. + +Examples: + govc device.info -vm $name + govc device.info -vm $name disk-* + govc device.info -vm $name -json ethernet-0 | jq -r .Devices[].MacAddress` +} + func (cmd *info) match(p string, devices object.VirtualDeviceList) object.VirtualDeviceList { var matches object.VirtualDeviceList match := func(name string) bool { - matched, _ := filepath.Match(p, name) + matched, _ := path.Match(p, name) return matched } @@ -125,7 +135,7 @@ func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error { } if f.NArg() == 0 { - res.Devices = devices + res.Devices = toInfoList(devices) } else { for _, name := range f.Args() { matches := cmd.match(name, devices) @@ -133,15 +143,41 @@ func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error { return fmt.Errorf("device '%s' not found", name) } - res.Devices = append(res.Devices, matches...) + res.Devices = append(res.Devices, toInfoList(matches)...) } } return cmd.WriteResult(&res) } +func toInfoList(devices object.VirtualDeviceList) []infoDevice { + var res []infoDevice + + for _, device := range devices { + res = append(res, infoDevice{devices.Name(device), device}) + } + + return res +} + +type infoDevice struct { + Name string + types.BaseVirtualDevice +} + +func (d *infoDevice) MarshalJSON() ([]byte, error) { + b, err := json.Marshal(d.BaseVirtualDevice) + if err != nil { + return b, err + } + + // TODO: make use of "inline" tag if it comes to be: https://github.com/golang/go/issues/6213 + + return append([]byte(fmt.Sprintf(`{"Name":"%s",`, d.Name)), b[1:]...), err +} + type infoResult struct { - Devices object.VirtualDeviceList + Devices []infoDevice // need the full list of devices to lookup attached devices and controllers list object.VirtualDeviceList } @@ -149,12 +185,13 @@ type infoResult struct { func (r *infoResult) Write(w io.Writer) error { tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0) - for _, device := range r.Devices { + for i := range r.Devices { + device := r.Devices[i].BaseVirtualDevice d := device.GetVirtualDevice() info := d.DeviceInfo.GetDescription() - fmt.Fprintf(tw, "Name:\t%s\n", r.Devices.Name(device)) - fmt.Fprintf(tw, " Type:\t%s\n", r.Devices.TypeName(device)) + fmt.Fprintf(tw, "Name:\t%s\n", r.Devices[i].Name) + fmt.Fprintf(tw, " Type:\t%s\n", r.list.TypeName(device)) fmt.Fprintf(tw, " Label:\t%s\n", info.Label) fmt.Fprintf(tw, " Summary:\t%s\n", info.Summary) fmt.Fprintf(tw, " Key:\t%d\n", d.Key) @@ -162,12 +199,12 @@ func (r *infoResult) Write(w io.Writer) error { if c, ok := device.(types.BaseVirtualController); ok { var attached []string for _, key := range c.GetVirtualController().Device { - attached = append(attached, r.Devices.Name(r.list.FindByKey(key))) + attached = append(attached, r.list.Name(r.list.FindByKey(key))) } fmt.Fprintf(tw, " Devices:\t%s\n", strings.Join(attached, ", ")) } else { if c := r.list.FindByKey(d.ControllerKey); c != nil { - fmt.Fprintf(tw, " Controller:\t%s\n", r.Devices.Name(c)) + fmt.Fprintf(tw, " Controller:\t%s\n", r.list.Name(c)) if d.UnitNumber != nil { fmt.Fprintf(tw, " Unit number:\t%d\n", *d.UnitNumber) } else { diff --git a/vendor/github.com/vmware/govmomi/govc/device/ls.go b/vendor/github.com/vmware/govmomi/govc/device/ls.go index a71add23..1775ebd3 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/ls.go +++ b/vendor/github.com/vmware/govmomi/govc/device/ls.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -51,6 +51,13 @@ func (cmd *ls) Process(ctx context.Context) error { return nil } +func (cmd *ls) Description() string { + return `List devices for VM. + +Examples: + govc device.ls -vm $name` +} + func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { vm, err := cmd.VirtualMachine() if err != nil { diff --git a/vendor/github.com/vmware/govmomi/govc/device/remove.go b/vendor/github.com/vmware/govmomi/govc/device/remove.go index cb531b3e..fb776fc0 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/remove.go +++ b/vendor/github.com/vmware/govmomi/govc/device/remove.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -51,6 +51,14 @@ func (cmd *remove) Usage() string { return "DEVICE..." } +func (cmd *remove) Description() string { + return `Remove DEVICE from VM. + +Examples: + govc device.remove -vm $name cdrom-3000 + govc device.remove -vm $name -keep disk-1000` +} + func (cmd *remove) Run(ctx context.Context, f *flag.FlagSet) error { vm, err := cmd.VirtualMachine() if err != nil { diff --git a/vendor/github.com/vmware/govmomi/govc/device/scsi/add.go b/vendor/github.com/vmware/govmomi/govc/device/scsi/add.go index 490d94c2..855fc5be 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/scsi/add.go +++ b/vendor/github.com/vmware/govmomi/govc/device/scsi/add.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/govc/device/serial/add.go b/vendor/github.com/vmware/govmomi/govc/device/serial/add.go index b8303388..d7206142 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/serial/add.go +++ b/vendor/github.com/vmware/govmomi/govc/device/serial/add.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/govc/device/serial/disconnect.go b/vendor/github.com/vmware/govmomi/govc/device/serial/disconnect.go index 6da1609b..5bc9c8d2 100644 --- a/vendor/github.com/vmware/govmomi/govc/device/serial/disconnect.go +++ b/vendor/github.com/vmware/govmomi/govc/device/serial/disconnect.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/govc/dvs/portgroup/info.go b/vendor/github.com/vmware/govmomi/govc/dvs/portgroup/info.go index 8c3df625..9cc8adb6 100644 --- a/vendor/github.com/vmware/govmomi/govc/dvs/portgroup/info.go +++ b/vendor/github.com/vmware/govmomi/govc/dvs/portgroup/info.go @@ -66,6 +66,18 @@ func (cmd *info) Process(ctx context.Context) error { return nil } +func (cmd *info) Usage() string { + return "DVS" +} + +func (cmd *info) Description() string { + return `Portgroup info for DVS. + +Examples: + govc dvs.portgroup.info DSwitch + govc find / -type DistributedVirtualSwitch | xargs -n1 govc dvs.portgroup.info` +} + func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error { if f.NArg() != 1 { return flag.ErrHelp diff --git a/vendor/github.com/vmware/govmomi/govc/flags/datacenter.go b/vendor/github.com/vmware/govmomi/govc/flags/datacenter.go index a776ed6e..8ae8787a 100644 --- a/vendor/github.com/vmware/govmomi/govc/flags/datacenter.go +++ b/vendor/github.com/vmware/govmomi/govc/flags/datacenter.go @@ -126,6 +126,13 @@ func (flag *DatacenterFlag) Datacenter() (*object.Datacenter, error) { return flag.dc, err } +func (flag *DatacenterFlag) DatacenterIfSpecified() (*object.Datacenter, error) { + if flag.path == "" { + return nil, nil + } + return flag.Datacenter() +} + func (flag *DatacenterFlag) ManagedObjects(ctx context.Context, args []string) ([]types.ManagedObjectReference, error) { var refs []types.ManagedObjectReference diff --git a/vendor/github.com/vmware/govmomi/govc/flags/int32.go b/vendor/github.com/vmware/govmomi/govc/flags/int32.go index 5ab58aa0..503b4ec4 100644 --- a/vendor/github.com/vmware/govmomi/govc/flags/int32.go +++ b/vendor/github.com/vmware/govmomi/govc/flags/int32.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2016 VMware, Inc. All Rights Reserved. +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. @@ -27,7 +27,7 @@ import ( type int32Value int32 func (i *int32Value) Set(s string) error { - v, err := strconv.ParseInt(s, 0, 64) + v, err := strconv.ParseInt(s, 0, 32) *i = int32Value(v) return err } @@ -44,3 +44,29 @@ func (i *int32Value) String() string { func NewInt32(v *int32) flag.Value { return (*int32Value)(v) } + +type int32ptrValue struct { + val **int32 +} + +func (i *int32ptrValue) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 32) + *i.val = new(int32) + **i.val = int32(v) + return err +} + +func (i *int32ptrValue) Get() interface{} { + if i.val == nil { + return -1 + } + return *i.val +} + +func (i *int32ptrValue) String() string { + return fmt.Sprintf("%v", i.Get()) +} + +func NewOptionalInt32(v **int32) flag.Value { + return &int32ptrValue{val: v} +} diff --git a/vendor/github.com/vmware/govmomi/govc/flags/output.go b/vendor/github.com/vmware/govmomi/govc/flags/output.go index 0ad0d95b..b02231d8 100644 --- a/vendor/github.com/vmware/govmomi/govc/flags/output.go +++ b/vendor/github.com/vmware/govmomi/govc/flags/output.go @@ -150,11 +150,14 @@ func (p *progressLogger) loopA() { tick := time.NewTicker(100 * time.Millisecond) defer tick.Stop() + called := false + for stop := false; !stop; { select { case ch := <-p.sink: err = p.loopB(tick, ch) stop = true + called = true case <-p.done: stop = true case <-tick.C: @@ -165,7 +168,7 @@ func (p *progressLogger) loopA() { if err != nil && err != io.EOF { p.flag.Log(fmt.Sprintf("\r%sError: %s\n", p.prefix, err)) - } else { + } else if called { p.flag.Log(fmt.Sprintf("\r%sOK\n", p.prefix)) } } diff --git a/vendor/github.com/vmware/govmomi/govc/flags/version.go b/vendor/github.com/vmware/govmomi/govc/flags/version.go index 65b9f39a..59761365 100644 --- a/vendor/github.com/vmware/govmomi/govc/flags/version.go +++ b/vendor/github.com/vmware/govmomi/govc/flags/version.go @@ -21,7 +21,7 @@ import ( "strings" ) -const Version = "0.12.1" +const Version = "0.15.0" type version []int diff --git a/vendor/github.com/vmware/govmomi/govc/host/autostart/add.go b/vendor/github.com/vmware/govmomi/govc/host/autostart/add.go index b453c7e1..4cabbd3d 100644 --- a/vendor/github.com/vmware/govmomi/govc/host/autostart/add.go +++ b/vendor/github.com/vmware/govmomi/govc/host/autostart/add.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2015 VMware, Inc. All Rights Reserved. +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. @@ -19,22 +19,49 @@ package autostart import ( "context" "flag" + "fmt" + "strings" "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" "github.com/vmware/govmomi/vim25/types" ) type add struct { *AutostartFlag + // from types.AutoStartPowerInfo + StartOrder int32 + StartDelay int32 + WaitForHeartbeat string + StartAction string + StopDelay int32 + StopAction string } func init() { cli.Register("host.autostart.add", &add{}) } +var waitHeartbeatTypes = []string{ + string(types.AutoStartWaitHeartbeatSettingSystemDefault), + string(types.AutoStartWaitHeartbeatSettingYes), + string(types.AutoStartWaitHeartbeatSettingNo), +} + func (cmd *add) Register(ctx context.Context, f *flag.FlagSet) { cmd.AutostartFlag, ctx = newAutostartFlag(ctx) cmd.AutostartFlag.Register(ctx, f) + + cmd.StartOrder = -1 + cmd.StartDelay = -1 + cmd.StopDelay = -1 + f.Var(flags.NewInt32(&cmd.StartOrder), "start-order", "Start Order") + f.Var(flags.NewInt32(&cmd.StartDelay), "start-delay", "Start Delay") + f.Var(flags.NewInt32(&cmd.StopDelay), "stop-delay", "Stop Delay") + f.StringVar(&cmd.StartAction, "start-action", "powerOn", "Start Action") + f.StringVar(&cmd.StopAction, "stop-action", "systemDefault", "Stop Action") + f.StringVar(&cmd.WaitForHeartbeat, "wait", waitHeartbeatTypes[0], + fmt.Sprintf("Wait for Hearbeat Setting (%s)", strings.Join(waitHeartbeatTypes, "|"))) } func (cmd *add) Process(ctx context.Context) error { @@ -49,14 +76,13 @@ func (cmd *add) Usage() string { } func (cmd *add) Run(ctx context.Context, f *flag.FlagSet) error { - var powerInfo = types.AutoStartPowerInfo{ - StartAction: "powerOn", - StartDelay: -1, - StartOrder: -1, - StopAction: "systemDefault", - StopDelay: -1, - WaitForHeartbeat: types.AutoStartWaitHeartbeatSettingSystemDefault, + powerInfo := types.AutoStartPowerInfo{ + StartOrder: cmd.StartOrder, + StartDelay: cmd.StartDelay, + WaitForHeartbeat: types.AutoStartWaitHeartbeatSetting(cmd.WaitForHeartbeat), + StartAction: cmd.StartAction, + StopDelay: cmd.StopDelay, + StopAction: cmd.StopAction, } - return cmd.ReconfigureVMs(f.Args(), powerInfo) } diff --git a/vendor/github.com/vmware/govmomi/govc/host/info.go b/vendor/github.com/vmware/govmomi/govc/host/info.go index ee05fcdf..ef482d4c 100644 --- a/vendor/github.com/vmware/govmomi/govc/host/info.go +++ b/vendor/github.com/vmware/govmomi/govc/host/info.go @@ -152,6 +152,11 @@ func (r *infoResult) Write(w io.Writer) error { fmt.Fprintf(tw, " Memory:\t%dMB\n", h.MemorySize/(1024*1024)) fmt.Fprintf(tw, " Memory usage:\t%d MB (%.1f%%)\n", z.OverallMemoryUsage, memUsage) fmt.Fprintf(tw, " Boot time:\t%s\n", s.Runtime.BootTime) + if s.Runtime.InMaintenanceMode { + fmt.Fprint(tw, " State: Maintenance Mode") + } else { + fmt.Fprintf(tw, " State:\t%s\n", s.Runtime.ConnectionState) + } } return tw.Flush() diff --git a/vendor/github.com/vmware/govmomi/govc/host/maintenance/enter.go b/vendor/github.com/vmware/govmomi/govc/host/maintenance/enter.go index 69758079..4b7fe9dc 100644 --- a/vendor/github.com/vmware/govmomi/govc/host/maintenance/enter.go +++ b/vendor/github.com/vmware/govmomi/govc/host/maintenance/enter.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package maintenancec +package maintenance import ( "context" diff --git a/vendor/github.com/vmware/govmomi/govc/host/maintenance/exit.go b/vendor/github.com/vmware/govmomi/govc/host/maintenance/exit.go index 879a4e17..6259033e 100644 --- a/vendor/github.com/vmware/govmomi/govc/host/maintenance/exit.go +++ b/vendor/github.com/vmware/govmomi/govc/host/maintenance/exit.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package maintenancec +package maintenance import ( "context" diff --git a/vendor/github.com/vmware/govmomi/govc/host/option/ls.go b/vendor/github.com/vmware/govmomi/govc/host/option/ls.go index 50f07648..9077492f 100644 --- a/vendor/github.com/vmware/govmomi/govc/host/option/ls.go +++ b/vendor/github.com/vmware/govmomi/govc/host/option/ls.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2016 VMware, Inc. All Rights Reserved. +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. @@ -19,19 +19,14 @@ package option import ( "context" "flag" - "fmt" - "io" - "os" - "text/tabwriter" "github.com/vmware/govmomi/govc/cli" "github.com/vmware/govmomi/govc/flags" - "github.com/vmware/govmomi/vim25/types" + "github.com/vmware/govmomi/govc/option" ) type ls struct { - *flags.ClientFlag - *flags.OutputFlag + *option.List *flags.HostSystemFlag } @@ -40,21 +35,19 @@ func init() { } func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) { - cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) - cmd.ClientFlag.Register(ctx, f) + cmd.List = &option.List{} + cmd.List.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.List.ClientFlag.Register(ctx, f) - cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx) - cmd.OutputFlag.Register(ctx, f) + cmd.List.OutputFlag, ctx = flags.NewOutputFlag(ctx) + cmd.List.OutputFlag.Register(ctx, f) cmd.HostSystemFlag, ctx = flags.NewHostSystemFlag(ctx) cmd.HostSystemFlag.Register(ctx, f) } func (cmd *ls) Process(ctx context.Context) error { - if err := cmd.ClientFlag.Process(ctx); err != nil { - return err - } - if err := cmd.OutputFlag.Process(ctx); err != nil { + if err := cmd.List.Process(ctx); err != nil { return err } if err := cmd.HostSystemFlag.Process(ctx); err != nil { @@ -63,21 +56,16 @@ func (cmd *ls) Process(ctx context.Context) error { return nil } -func (cmd *ls) Usage() string { - return "NAME" -} - func (cmd *ls) Description() string { - return `List option with the given NAME. + return option.ListDescription + ` -If NAME ends with a dot, all options for that subtree are listed.` +Examples: + govc host.option.ls + govc host.option.ls Config.HostAgent. + govc host.option.ls Config.HostAgent.plugins.solo.enableMob` } func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { - if f.NArg() != 1 { - return flag.ErrHelp - } - host, err := cmd.HostSystem() if err != nil { return err @@ -88,21 +76,5 @@ func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { return err } - opts, err := m.Query(ctx, f.Arg(0)) - if err != nil { - return err - } - - return cmd.WriteResult(optionResult(opts)) -} - -type optionResult []types.BaseOptionValue - -func (r optionResult) Write(w io.Writer) error { - tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0) - for _, opt := range r { - o := opt.GetOptionValue() - fmt.Fprintf(tw, "%s:\t%v\n", o.Key, o.Value) - } - return tw.Flush() + return cmd.Query(ctx, f, m) } diff --git a/vendor/github.com/vmware/govmomi/govc/host/option/set.go b/vendor/github.com/vmware/govmomi/govc/host/option/set.go index 5e4f24a5..a347718b 100644 --- a/vendor/github.com/vmware/govmomi/govc/host/option/set.go +++ b/vendor/github.com/vmware/govmomi/govc/host/option/set.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2016 VMware, Inc. All Rights Reserved. +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. @@ -19,16 +19,14 @@ package option import ( "context" "flag" - "fmt" - "strconv" "github.com/vmware/govmomi/govc/cli" "github.com/vmware/govmomi/govc/flags" - "github.com/vmware/govmomi/vim25/types" + "github.com/vmware/govmomi/govc/option" ) type set struct { - *flags.ClientFlag + *option.Set *flags.HostSystemFlag } @@ -37,15 +35,16 @@ func init() { } func (cmd *set) Register(ctx context.Context, f *flag.FlagSet) { - cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) - cmd.ClientFlag.Register(ctx, f) + cmd.Set = &option.Set{} + cmd.Set.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.Set.ClientFlag.Register(ctx, f) cmd.HostSystemFlag, ctx = flags.NewHostSystemFlag(ctx) cmd.HostSystemFlag.Register(ctx, f) } func (cmd *set) Process(ctx context.Context) error { - if err := cmd.ClientFlag.Process(ctx); err != nil { + if err := cmd.Set.Process(ctx); err != nil { return err } if err := cmd.HostSystemFlag.Process(ctx); err != nil { @@ -54,12 +53,8 @@ func (cmd *set) Process(ctx context.Context) error { return nil } -func (cmd *set) Usage() string { - return "NAME VALUE" -} - func (cmd *set) Description() string { - return `Set host option NAME to VALUE. + return option.SetDescription + ` Examples: govc host.option.set Config.HostAgent.plugins.solo.enableMob true @@ -77,47 +72,5 @@ func (cmd *set) Run(ctx context.Context, f *flag.FlagSet) error { return err } - if f.NArg() != 2 { - return flag.ErrHelp - } - - name := f.Arg(0) - opts, err := m.Query(ctx, name) - if err != nil { - return err - } - - if len(opts) != 1 { - return flag.ErrHelp - } - - val := f.Arg(1) - var set types.AnyType - - switch x := opts[0].GetOptionValue().Value.(type) { - case string: - set = val - case bool: - set, err = strconv.ParseBool(val) - if err != nil { - return err - } - case int32: - s, err := strconv.ParseInt(val, 10, 32) - if err != nil { - return err - } - set = s - case int64: - set, err = strconv.ParseInt(val, 10, 64) - if err != nil { - return err - } - default: - return fmt.Errorf("type %T conversion not supported", x) - } - - opts[0].GetOptionValue().Value = set - - return m.Update(ctx, opts) + return cmd.Update(ctx, f, m) } diff --git a/vendor/github.com/vmware/govmomi/govc/host/shutdown.go b/vendor/github.com/vmware/govmomi/govc/host/shutdown.go new file mode 100644 index 00000000..97d1b01a --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/host/shutdown.go @@ -0,0 +1,119 @@ +/* +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 host + +import ( + "context" + "flag" + "fmt" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/types" +) + +type shutdown struct { + *flags.HostSystemFlag + force bool + reboot bool +} + +func init() { + cli.Register("host.shutdown", &shutdown{}) +} + +func (cmd *shutdown) Register(ctx context.Context, f *flag.FlagSet) { + cmd.HostSystemFlag, ctx = flags.NewHostSystemFlag(ctx) + cmd.HostSystemFlag.Register(ctx, f) + + f.BoolVar(&cmd.force, "f", false, "Force shutdown when host is not in maintenance mode") + f.BoolVar(&cmd.reboot, "r", false, "Reboot host") +} + +func (cmd *shutdown) Process(ctx context.Context) error { + if err := cmd.HostSystemFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *shutdown) Description() string { + return `Shutdown HOST.` +} + +func (cmd *shutdown) Shutdown(ctx context.Context, host *object.HostSystem) error { + req := types.ShutdownHost_Task{ + This: host.Reference(), + Force: cmd.force, + } + + res, err := methods.ShutdownHost_Task(ctx, host.Client(), &req) + if err != nil { + return err + } + + task := object.NewTask(host.Client(), res.Returnval) + + logger := cmd.ProgressLogger(fmt.Sprintf("%s shutdown... ", host.InventoryPath)) + defer logger.Wait() + + _, err = task.WaitForResult(ctx, logger) + return err +} + +func (cmd *shutdown) Reboot(ctx context.Context, host *object.HostSystem) error { + req := types.RebootHost_Task{ + This: host.Reference(), + Force: cmd.force, + } + + res, err := methods.RebootHost_Task(ctx, host.Client(), &req) + if err != nil { + return err + } + + task := object.NewTask(host.Client(), res.Returnval) + + logger := cmd.ProgressLogger(fmt.Sprintf("%s reboot... ", host.InventoryPath)) + defer logger.Wait() + + _, err = task.WaitForResult(ctx, logger) + return err +} + +func (cmd *shutdown) Run(ctx context.Context, f *flag.FlagSet) error { + hosts, err := cmd.HostSystems(f.Args()) + if err != nil { + return err + } + + s := cmd.Shutdown + if cmd.reboot { + s = cmd.Reboot + } + + for _, host := range hosts { + err = s(ctx, host) + if err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/vmware/govmomi/govc/host/vnic/service.go b/vendor/github.com/vmware/govmomi/govc/host/vnic/service.go index c80071f4..f759ab7a 100644 --- a/vendor/github.com/vmware/govmomi/govc/host/vnic/service.go +++ b/vendor/github.com/vmware/govmomi/govc/host/vnic/service.go @@ -30,8 +30,7 @@ import ( type service struct { *flags.HostSystemFlag - Enable bool - Disable bool + Enable bool } func init() { @@ -42,18 +41,13 @@ func (cmd *service) Register(ctx context.Context, f *flag.FlagSet) { cmd.HostSystemFlag, ctx = flags.NewHostSystemFlag(ctx) cmd.HostSystemFlag.Register(ctx, f) - f.BoolVar(&cmd.Enable, "enable", false, "Enable service") - f.BoolVar(&cmd.Disable, "disable", false, "Disable service") + f.BoolVar(&cmd.Enable, "enable", true, "Enable service") } func (cmd *service) Process(ctx context.Context) error { if err := cmd.HostSystemFlag.Process(ctx); err != nil { return err } - // Either may be true or none may be true. - if cmd.Enable && cmd.Disable { - return flag.ErrHelp - } return nil } @@ -81,7 +75,7 @@ Where DEVICE is one of: %s Examples: govc host.vnic.service -host hostname -enable vsan vmk0 -`, + govc host.vnic.service -host hostname -enable=false vmotion vmk1`, strings.Join(nicTypes, "|"), strings.Join([]string{"vmk0", "vmk1", "..."}, "|")) } @@ -108,7 +102,7 @@ func (cmd *service) Run(ctx context.Context, f *flag.FlagSet) error { if cmd.Enable { method = m.SelectVnic - } else if cmd.Disable { + } else { method = m.DeselectVnic } diff --git a/vendor/github.com/vmware/govmomi/govc/importx/archive.go b/vendor/github.com/vmware/govmomi/govc/importx/archive.go index a77d542f..ff8499d0 100644 --- a/vendor/github.com/vmware/govmomi/govc/importx/archive.go +++ b/vendor/github.com/vmware/govmomi/govc/importx/archive.go @@ -59,7 +59,7 @@ func (f *ArchiveFlag) ReadOvf(fpath string) ([]byte, error) { func (f *ArchiveFlag) ReadEnvelope(fpath string) (*ovf.Envelope, error) { if fpath == "" { - return nil, nil + return &ovf.Envelope{}, nil } r, _, err := f.Open(fpath) @@ -110,7 +110,7 @@ func (t *TapeArchive) Open(name string) (io.ReadCloser, int64, error) { return nil, 0, err } - matched, err := filepath.Match(name, path.Base(h.Name)) + matched, err := path.Match(name, path.Base(h.Name)) if err != nil { return nil, 0, err } diff --git a/vendor/github.com/vmware/govmomi/govc/importx/folder.go b/vendor/github.com/vmware/govmomi/govc/importx/folder.go deleted file mode 100644 index 427c29b1..00000000 --- a/vendor/github.com/vmware/govmomi/govc/importx/folder.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -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 importx - -import ( - "context" - "errors" - "flag" - - "github.com/vmware/govmomi/govc/flags" - "github.com/vmware/govmomi/object" -) - -type FolderFlag struct { - *flags.DatacenterFlag - - folder string -} - -func newFolderFlag(ctx context.Context) (*FolderFlag, context.Context) { - f := &FolderFlag{} - f.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx) - return f, ctx -} - -func (flag *FolderFlag) Register(ctx context.Context, f *flag.FlagSet) { - flag.DatacenterFlag.Register(ctx, f) - - f.StringVar(&flag.folder, "folder", "", "Path to folder to add the VM to") -} - -func (flag *FolderFlag) Process(ctx context.Context) error { - return flag.DatacenterFlag.Process(ctx) -} - -func (flag *FolderFlag) Folder() (*object.Folder, error) { - ctx := context.TODO() - if len(flag.folder) == 0 { - dc, err := flag.Datacenter() - if err != nil { - return nil, err - } - folders, err := dc.Folders(ctx) - if err != nil { - return nil, err - } - return folders.VmFolder, nil - } - - finder, err := flag.Finder() - if err != nil { - return nil, err - } - - mo, err := finder.ManagedObjectList(ctx, flag.folder) - if err != nil { - return nil, err - } - if len(mo) == 0 { - return nil, errors.New("folder argument does not resolve to object") - } - if len(mo) > 1 { - return nil, errors.New("folder argument resolves to more than one object") - } - - ref := mo[0].Object.Reference() - if ref.Type != "Folder" { - return nil, errors.New("folder argument does not resolve to folder") - } - - c, err := flag.Client() - if err != nil { - return nil, err - } - - return object.NewFolder(c, ref), nil -} diff --git a/vendor/github.com/vmware/govmomi/govc/importx/ovf.go b/vendor/github.com/vmware/govmomi/govc/importx/ovf.go index dd1b211a..debbc48a 100644 --- a/vendor/github.com/vmware/govmomi/govc/importx/ovf.go +++ b/vendor/github.com/vmware/govmomi/govc/importx/ovf.go @@ -25,10 +25,10 @@ import ( "github.com/vmware/govmomi/govc/cli" "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/nfc" "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/ovf" "github.com/vmware/govmomi/vim25" - "github.com/vmware/govmomi/vim25/progress" "github.com/vmware/govmomi/vim25/soap" "github.com/vmware/govmomi/vim25/types" ) @@ -38,10 +38,10 @@ type ovfx struct { *flags.HostSystemFlag *flags.OutputFlag *flags.ResourcePoolFlag + *flags.FolderFlag *ArchiveFlag *OptionsFlag - *FolderFlag Name string @@ -64,13 +64,13 @@ func (cmd *ovfx) Register(ctx context.Context, f *flag.FlagSet) { cmd.OutputFlag.Register(ctx, f) cmd.ResourcePoolFlag, ctx = flags.NewResourcePoolFlag(ctx) cmd.ResourcePoolFlag.Register(ctx, f) + cmd.FolderFlag, ctx = flags.NewFolderFlag(ctx) + cmd.FolderFlag.Register(ctx, f) cmd.ArchiveFlag, ctx = newArchiveFlag(ctx) cmd.ArchiveFlag.Register(ctx, f) cmd.OptionsFlag, ctx = newOptionsFlag(ctx) cmd.OptionsFlag.Register(ctx, f) - cmd.FolderFlag, ctx = newFolderFlag(ctx) - cmd.FolderFlag.Register(ctx, f) f.StringVar(&cmd.Name, "name", "", "Name to use for new entity") } @@ -248,7 +248,7 @@ func (cmd *ovfx) Import(fpath string) (*types.ManagedObjectReference, error) { NetworkMapping: cmd.NetworkMap(e), } - m := object.NewOvfManager(cmd.Client) + m := ovf.NewManager(cmd.Client) spec, err := m.CreateImportSpec(ctx, string(o), cmd.ResourcePool, cmd.Datastore, cisp) if err != nil { return nil, err @@ -278,7 +278,7 @@ func (cmd *ovfx) Import(fpath string) (*types.ManagedObjectReference, error) { } } - folder, err := cmd.Folder() + folder, err := cmd.FolderOrDefault("vm") if err != nil { return nil, err } @@ -288,51 +288,25 @@ func (cmd *ovfx) Import(fpath string) (*types.ManagedObjectReference, error) { return nil, err } - info, err := lease.Wait(ctx) + info, err := lease.Wait(ctx, spec.FileItem) if err != nil { return nil, err } - // Build slice of items and URLs first, so that the lease updater can know - // about every item that needs to be uploaded, and thereby infer progress. - var items []ovfFileItem - - for _, device := range info.DeviceUrl { - for _, item := range spec.FileItem { - if device.ImportKey != item.DeviceId { - continue - } - - u, err := cmd.Client.ParseURL(device.Url) - if err != nil { - return nil, err - } - - i := ovfFileItem{ - url: u, - item: item, - ch: make(chan progress.Report), - } - - items = append(items, i) - } - } - - u := newLeaseUpdater(cmd.Client, lease, items) + u := lease.StartUpdater(ctx, info) defer u.Done() - for _, i := range items { - err = cmd.Upload(lease, i) + for _, i := range info.Items { + err = cmd.Upload(ctx, lease, i) if err != nil { return nil, err } } - return &info.Entity, lease.HttpNfcLeaseComplete(ctx) + return &info.Entity, lease.Complete(ctx) } -func (cmd *ovfx) Upload(lease *object.HttpNfcLease, ofi ovfFileItem) error { - item := ofi.item +func (cmd *ovfx) Upload(ctx context.Context, lease *nfc.Lease, item nfc.FileItem) error { file := item.Path f, size, err := cmd.Open(file) @@ -346,22 +320,10 @@ func (cmd *ovfx) Upload(lease *object.HttpNfcLease, ofi ovfFileItem) error { opts := soap.Upload{ ContentLength: size, - Progress: progress.Tee(ofi, logger), - } - - // Non-disk files (such as .iso) use the PUT method. - // Overwrite: t header is also required in this case (ovftool does the same) - if item.Create { - opts.Method = "PUT" - opts.Headers = map[string]string{ - "Overwrite": "t", - } - } else { - opts.Method = "POST" - opts.Type = "application/x-vnd.vmware-streamVmdk" + Progress: logger, } - return cmd.Client.Client.Upload(f, ofi.url, &opts) + return lease.Upload(ctx, item, f, opts) } func (cmd *ovfx) PowerOn(vm *object.VirtualMachine) error { diff --git a/vendor/github.com/vmware/govmomi/govc/importx/spec.go b/vendor/github.com/vmware/govmomi/govc/importx/spec.go index 4245403a..c490a28b 100644 --- a/vendor/github.com/vmware/govmomi/govc/importx/spec.go +++ b/vendor/github.com/vmware/govmomi/govc/importx/spec.go @@ -88,7 +88,7 @@ func (cmd *spec) Run(ctx context.Context, f *flag.FlagSet) error { } func (cmd *spec) Map(e *ovf.Envelope) (res []Property) { - if e == nil { + if e == nil || e.VirtualSystem == nil { return nil } @@ -128,7 +128,7 @@ func (cmd *spec) Spec(fpath string) error { } var deploymentOptions = allDeploymentOptions - if e != nil && e.DeploymentOption != nil && e.DeploymentOption.Configuration != nil { + if e.DeploymentOption != nil && e.DeploymentOption.Configuration != nil { deploymentOptions = nil // add default first @@ -155,7 +155,7 @@ func (cmd *spec) Spec(fpath string) error { InjectOvfEnv: false, PropertyMapping: cmd.Map(e)} - if e.VirtualSystem.Annotation != nil { + if e.VirtualSystem != nil && e.VirtualSystem.Annotation != nil { for _, a := range e.VirtualSystem.Annotation { o.Annotation += a.Annotation } diff --git a/vendor/github.com/vmware/govmomi/govc/importx/vmdk.go b/vendor/github.com/vmware/govmomi/govc/importx/vmdk.go index 270e37b0..6e0ac5f9 100644 --- a/vendor/github.com/vmware/govmomi/govc/importx/vmdk.go +++ b/vendor/github.com/vmware/govmomi/govc/importx/vmdk.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved. +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. @@ -22,483 +22,111 @@ import ( "flag" "fmt" "path" - "reflect" - "regexp" "github.com/vmware/govmomi/govc/cli" "github.com/vmware/govmomi/govc/flags" - "github.com/vmware/govmomi/object" - "github.com/vmware/govmomi/property" - "github.com/vmware/govmomi/vim25" - "github.com/vmware/govmomi/vim25/mo" - "github.com/vmware/govmomi/vim25/progress" - "github.com/vmware/govmomi/vim25/soap" - "github.com/vmware/govmomi/vim25/types" + "github.com/vmware/govmomi/vmdk" ) -type vmdk struct { +type disk struct { *flags.DatastoreFlag *flags.ResourcePoolFlag + *flags.FolderFlag *flags.OutputFlag - upload bool - force bool - keep bool - - Client *vim25.Client - Datacenter *object.Datacenter - Datastore *object.Datastore - ResourcePool *object.ResourcePool + force bool } func init() { - cli.Register("import.vmdk", &vmdk{}) - cli.Alias("import.vmdk", "datastore.import") + cli.Register("import.vmdk", &disk{}) } -func (cmd *vmdk) Register(ctx context.Context, f *flag.FlagSet) { +func (cmd *disk) Register(ctx context.Context, f *flag.FlagSet) { cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx) cmd.DatastoreFlag.Register(ctx, f) cmd.ResourcePoolFlag, ctx = flags.NewResourcePoolFlag(ctx) cmd.ResourcePoolFlag.Register(ctx, f) + cmd.FolderFlag, ctx = flags.NewFolderFlag(ctx) + cmd.FolderFlag.Register(ctx, f) cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx) cmd.OutputFlag.Register(ctx, f) - f.BoolVar(&cmd.upload, "upload", true, "Upload specified disk") f.BoolVar(&cmd.force, "force", false, "Overwrite existing disk") - f.BoolVar(&cmd.keep, "keep", false, "Keep uploaded disk after import") } -func (cmd *vmdk) Process(ctx context.Context) error { +func (cmd *disk) Process(ctx context.Context) error { if err := cmd.DatastoreFlag.Process(ctx); err != nil { return err } if err := cmd.ResourcePoolFlag.Process(ctx); err != nil { return err } + if err := cmd.FolderFlag.Process(ctx); err != nil { + return err + } if err := cmd.OutputFlag.Process(ctx); err != nil { return err } return nil } -func (cmd *vmdk) Usage() string { +func (cmd *disk) Usage() string { return "PATH_TO_VMDK [REMOTE_DIRECTORY]" } -func (cmd *vmdk) Run(ctx context.Context, f *flag.FlagSet) error { - var err error - +func (cmd *disk) Run(ctx context.Context, f *flag.FlagSet) error { args := f.Args() if len(args) < 1 { return errors.New("no file to import") } - file := importable{ - localPath: f.Arg(0), - } - - // Include remote path if specified - if len(args) >= 2 { - file.remotePath = f.Arg(1) - } + src := f.Arg(0) - cmd.Client, err = cmd.DatastoreFlag.Client() + c, err := cmd.DatastoreFlag.Client() if err != nil { return err } - cmd.Datacenter, err = cmd.DatastoreFlag.Datacenter() + dc, err := cmd.DatastoreFlag.Datacenter() if err != nil { return err } - cmd.Datastore, err = cmd.DatastoreFlag.Datastore() + ds, err := cmd.DatastoreFlag.Datastore() if err != nil { return err } - cmd.ResourcePool, err = cmd.ResourcePoolFlag.ResourcePool() + pool, err := cmd.ResourcePoolFlag.ResourcePool() if err != nil { return err } - err = cmd.PrepareDestination(file) + folder, err := cmd.FolderOrDefault("vm") if err != nil { return err } - if cmd.upload { - err = cmd.Upload(file) - if err != nil { - return err - } - } - - return cmd.Import(file) -} - -// PrepareDestination makes sure that the destination VMDK does not yet exist. -// If the force flag is passed, it removes the existing VMDK. This functions -// exists to give a meaningful error if the remote VMDK already exists. -// -// CopyVirtualDisk can return a " file does not exist" error while in fact -// the source file *does* exist and the *destination* file also exist. -// -func (cmd *vmdk) PrepareDestination(i importable) error { - ctx := context.TODO() - vmdkPath := i.RemoteDstVMDK() - res, err := cmd.Datastore.Stat(ctx, vmdkPath) - if err != nil { - switch err.(type) { - case object.DatastoreNoSuchDirectoryError: - // The base path doesn't exist. Create it. - dsPath := cmd.Datastore.Path(path.Dir(vmdkPath)) - m := object.NewFileManager(cmd.Client) - return m.MakeDirectory(ctx, dsPath, cmd.Datacenter, true) - case object.DatastoreNoSuchFileError: - // Destination path doesn't exist; all good to continue with import. - return nil - } - - return err - } - - // Check that the returned entry has the right type. - switch res.(type) { - case *types.VmDiskFileInfo: - default: - expected := "VmDiskFileInfo" - actual := reflect.TypeOf(res) - panic(fmt.Sprintf("Expected: %s, actual: %s", expected, actual)) - } - - if !cmd.force { - dsPath := cmd.Datastore.Path(vmdkPath) - err = fmt.Errorf("File %s already exists", dsPath) - return err - } - - // Delete existing disk. - err = cmd.DeleteDisk(vmdkPath) - if err != nil { - return err - } - - return nil -} - -func (cmd *vmdk) Upload(i importable) error { - ctx := context.TODO() - p := soap.DefaultUpload - if cmd.OutputFlag.TTY { - logger := cmd.ProgressLogger("Uploading... ") - p.Progress = logger - defer logger.Wait() - } - - return cmd.Datastore.UploadFile(ctx, i.localPath, i.RemoteSrcVMDK(), &p) -} - -func (cmd *vmdk) Import(i importable) error { - err := cmd.Copy(i) - if err != nil { - return err - } - - if !cmd.keep { - err = cmd.DeleteDisk(i.RemoteSrcVMDK()) - if err != nil { - return err - } - } - - return nil -} - -func (cmd *vmdk) Copy(i importable) error { - var err error - - logger := cmd.ProgressLogger("Importing... ") + logger := cmd.ProgressLogger(fmt.Sprintf("Uploading %s... ", path.Base(src))) defer logger.Wait() - agg := progress.NewAggregator(logger) - defer agg.Done() - - switch p := cmd.Client.ServiceContent.About.ApiType; p { - case "HostAgent": - err = cmd.CopyHostAgent(i, agg) - case "VirtualCenter": - err = cmd.CopyVirtualCenter(i, agg) - default: - return fmt.Errorf("unsupported ApiType: %s", p) + p := vmdk.ImportParams{ + Path: f.Arg(1), + Logger: logger, + Type: "", // TODO: flag + Force: cmd.force, + Datacenter: dc, + Pool: pool, + Folder: folder, } - return err -} - -func (cmd *vmdk) CopyHostAgent(i importable, s progress.Sinker) error { - ctx := context.TODO() - spec := &types.VirtualDiskSpec{ - AdapterType: "lsiLogic", - DiskType: "thin", - } - - dc := cmd.Datacenter - src := cmd.Datastore.Path(i.RemoteSrcVMDK()) - dst := cmd.Datastore.Path(i.RemoteDstVMDK()) - vdm := object.NewVirtualDiskManager(cmd.Client) - task, err := vdm.CopyVirtualDisk(ctx, src, dc, dst, dc, spec, false) - if err != nil { - return err - } - - ps := progress.Prefix(s, "copying disk") - _, err = task.WaitForResult(ctx, ps) - if err != nil { - return err - } - - return nil -} - -func (cmd *vmdk) CopyVirtualCenter(i importable, s progress.Sinker) error { - var err error - - srcName := i.BaseClean() + "-srcvm" - dstName := i.BaseClean() + "-dstvm" - - spec := &configSpec{ - Name: srcName, - GuestId: "otherGuest", - Files: &types.VirtualMachineFileInfo{ - VmPathName: fmt.Sprintf("[%s]", cmd.Datastore.Name()), - }, - } - - spec.AddDisk(cmd.Datastore, i.RemoteSrcVMDK()) - - src, err := cmd.CreateVM(spec) - if err != nil { - return err - } - - dst, err := cmd.CloneVM(src, dstName) - if err != nil { - return err - } - - err = cmd.DestroyVM(src) - if err != nil { - return err - } - - vmdk, err := cmd.DetachDisk(dst) - if err != nil { - return err + err = vmdk.Import(ctx, c, src, ds, p) + if err != nil && err == vmdk.ErrInvalidFormat { + return fmt.Errorf(`%s +The vmdk can be converted using one of: + vmware-vdiskmanager -t 5 -r '%s' new.vmdk + qemu-img convert -O vmdk -o subformat=streamOptimized '%s' new.vmdk`, err, src, src) } - err = cmd.MoveDisk(vmdk, i.RemoteDstVMDK()) - if err != nil { - return err - } - - err = cmd.DestroyVM(dst) - if err != nil { - return err - } - - return nil -} - -func (cmd *vmdk) MoveDisk(src, dst string) error { - ctx := context.TODO() - dsSrc := cmd.Datastore.Path(src) - dsDst := cmd.Datastore.Path(dst) - vdm := object.NewVirtualDiskManager(cmd.Client) - task, err := vdm.MoveVirtualDisk(ctx, dsSrc, cmd.Datacenter, dsDst, cmd.Datacenter, true) - if err != nil { - return err - } - - return task.Wait(ctx) -} - -func (cmd *vmdk) DeleteDisk(path string) error { - ctx := context.TODO() - vdm := object.NewVirtualDiskManager(cmd.Client) - task, err := vdm.DeleteVirtualDisk(ctx, cmd.Datastore.Path(path), cmd.Datacenter) - if err != nil { - return err - } - - return task.Wait(ctx) -} - -func (cmd *vmdk) DetachDisk(vm *object.VirtualMachine) (string, error) { - ctx := context.TODO() - var mvm mo.VirtualMachine - - pc := property.DefaultCollector(cmd.Client) - err := pc.RetrieveOne(ctx, vm.Reference(), []string{"config.hardware"}, &mvm) - if err != nil { - return "", err - } - - spec := new(configSpec) - dsFile := spec.RemoveDisk(&mvm) - - task, err := vm.Reconfigure(ctx, spec.ToSpec()) - if err != nil { - return "", err - } - - err = task.Wait(ctx) - if err != nil { - return "", err - } - - return dsFile, nil -} - -func (cmd *vmdk) CreateVM(spec *configSpec) (*object.VirtualMachine, error) { - ctx := context.TODO() - folders, err := cmd.Datacenter.Folders(ctx) - if err != nil { - return nil, err - } - - task, err := folders.VmFolder.CreateVM(ctx, spec.ToSpec(), cmd.ResourcePool, nil) - if err != nil { - return nil, err - } - - info, err := task.WaitForResult(ctx, nil) - if err != nil { - return nil, err - } - - return object.NewVirtualMachine(cmd.Client, info.Result.(types.ManagedObjectReference)), nil -} - -func (cmd *vmdk) CloneVM(vm *object.VirtualMachine, name string) (*object.VirtualMachine, error) { - ctx := context.TODO() - folders, err := cmd.Datacenter.Folders(ctx) - if err != nil { - return nil, err - } - - spec := types.VirtualMachineCloneSpec{ - Config: &types.VirtualMachineConfigSpec{}, - Location: types.VirtualMachineRelocateSpec{}, - } - - task, err := vm.Clone(ctx, folders.VmFolder, name, spec) - if err != nil { - return nil, err - } - - info, err := task.WaitForResult(ctx, nil) - if err != nil { - return nil, err - } - - return object.NewVirtualMachine(cmd.Client, info.Result.(types.ManagedObjectReference)), nil -} - -func (cmd *vmdk) DestroyVM(vm *object.VirtualMachine) error { - ctx := context.TODO() - _, err := cmd.DetachDisk(vm) - if err != nil { - return err - } - - task, err := vm.Destroy(ctx) - if err != nil { - return err - } - - err = task.Wait(ctx) - if err != nil { - return err - } - - return nil -} - -type configSpec types.VirtualMachineConfigSpec - -func (c *configSpec) ToSpec() types.VirtualMachineConfigSpec { - return types.VirtualMachineConfigSpec(*c) -} - -func (c *configSpec) AddChange(d types.BaseVirtualDeviceConfigSpec) { - c.DeviceChange = append(c.DeviceChange, d) -} - -func (c *configSpec) AddDisk(ds *object.Datastore, path string) { - var devices object.VirtualDeviceList - - controller, err := devices.CreateSCSIController("") - if err != nil { - panic(err) - } - devices = append(devices, controller) - - disk := devices.CreateDisk(controller.(types.BaseVirtualController), ds.Reference(), ds.Path(path)) - devices = append(devices, disk) - - spec, err := devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd) - if err != nil { - panic(err) - } - - c.DeviceChange = append(c.DeviceChange, spec...) -} - -var dsPathRegexp = regexp.MustCompile(`^\[.*\] (.*)$`) - -func (c *configSpec) RemoveDisk(vm *mo.VirtualMachine) string { - var file string - - for _, d := range vm.Config.Hardware.Device { - switch device := d.(type) { - case *types.VirtualDisk: - if file != "" { - panic("expected VM to have only one disk") - } - - switch backing := device.Backing.(type) { - case *types.VirtualDiskFlatVer1BackingInfo: - file = backing.FileName - case *types.VirtualDiskFlatVer2BackingInfo: - file = backing.FileName - case *types.VirtualDiskSeSparseBackingInfo: - file = backing.FileName - case *types.VirtualDiskSparseVer1BackingInfo: - file = backing.FileName - case *types.VirtualDiskSparseVer2BackingInfo: - file = backing.FileName - default: - name := reflect.TypeOf(device.Backing).String() - panic(fmt.Sprintf("unexpected backing type: %s", name)) - } - - // Remove [datastore] prefix - m := dsPathRegexp.FindStringSubmatch(file) - if len(m) != 2 { - panic(fmt.Sprintf("expected regexp match for %#v", file)) - } - file = m[1] - - removeOp := &types.VirtualDeviceConfigSpec{ - Operation: types.VirtualDeviceConfigSpecOperationRemove, - Device: device, - } - - c.AddChange(removeOp) - } - } - - return file + return err } diff --git a/vendor/github.com/vmware/govmomi/govc/ls/command.go b/vendor/github.com/vmware/govmomi/govc/ls/command.go index bbd20196..06a9b409 100644 --- a/vendor/github.com/vmware/govmomi/govc/ls/command.go +++ b/vendor/github.com/vmware/govmomi/govc/ls/command.go @@ -99,6 +99,12 @@ func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { var ref = new(types.ManagedObjectReference) + var types []string + if cmd.Type != "" { + // TODO: support multiple -t flags + types = []string{cmd.Type} + } + for _, arg := range args { if cmd.DeRef && ref.FromString(arg) { e, err := finder.Element(ctx, *ref) @@ -115,7 +121,7 @@ func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { } } - es, err := finder.ManagedObjectListChildren(ctx, arg) + es, err := finder.ManagedObjectListChildren(ctx, arg, types...) if err != nil { return err } diff --git a/vendor/github.com/vmware/govmomi/govc/main.go b/vendor/github.com/vmware/govmomi/govc/main.go index 474b5c4c..4931b5ed 100644 --- a/vendor/github.com/vmware/govmomi/govc/main.go +++ b/vendor/github.com/vmware/govmomi/govc/main.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -25,6 +25,8 @@ import ( _ "github.com/vmware/govmomi/govc/cluster" _ "github.com/vmware/govmomi/govc/datacenter" _ "github.com/vmware/govmomi/govc/datastore" + _ "github.com/vmware/govmomi/govc/datastore/disk" + _ "github.com/vmware/govmomi/govc/datastore/vsan" _ "github.com/vmware/govmomi/govc/device" _ "github.com/vmware/govmomi/govc/device/cdrom" _ "github.com/vmware/govmomi/govc/device/floppy" @@ -56,11 +58,15 @@ import ( _ "github.com/vmware/govmomi/govc/license" _ "github.com/vmware/govmomi/govc/logs" _ "github.com/vmware/govmomi/govc/ls" + _ "github.com/vmware/govmomi/govc/metric" + _ "github.com/vmware/govmomi/govc/metric/interval" _ "github.com/vmware/govmomi/govc/object" + _ "github.com/vmware/govmomi/govc/option" _ "github.com/vmware/govmomi/govc/permissions" _ "github.com/vmware/govmomi/govc/pool" _ "github.com/vmware/govmomi/govc/role" _ "github.com/vmware/govmomi/govc/session" + _ "github.com/vmware/govmomi/govc/task" _ "github.com/vmware/govmomi/govc/vapp" _ "github.com/vmware/govmomi/govc/version" _ "github.com/vmware/govmomi/govc/vm" diff --git a/vendor/github.com/vmware/govmomi/govc/metric/change.go b/vendor/github.com/vmware/govmomi/govc/metric/change.go new file mode 100644 index 00000000..1a69ce3b --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/metric/change.go @@ -0,0 +1,101 @@ +/* +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 metric + +import ( + "context" + "flag" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/types" +) + +type change struct { + *PerformanceFlag + + level int + device int +} + +func init() { + cli.Register("metric.change", &change{}) +} + +func (cmd *change) Register(ctx context.Context, f *flag.FlagSet) { + cmd.PerformanceFlag, ctx = NewPerformanceFlag(ctx) + cmd.PerformanceFlag.Register(ctx, f) + + f.IntVar(&cmd.level, "level", 0, "Level for the aggregate counter") + f.IntVar(&cmd.device, "device-level", 0, "Level for the per device counter") +} + +func (cmd *change) Usage() string { + return "NAME..." +} + +func (cmd *change) Description() string { + return `Change counter NAME levels. + +Examples: + govc metric.change -level 1 net.bytesRx.average net.bytesTx.average` +} + +func (cmd *change) Process(ctx context.Context) error { + if err := cmd.PerformanceFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *change) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() == 0 || (cmd.level == 0 && cmd.device == 0) { + return flag.ErrHelp + } + + m, err := cmd.Manager(ctx) + if err != nil { + return err + } + + counters, err := m.CounterInfoByName(ctx) + if err != nil { + return err + } + + var mapping []types.PerformanceManagerCounterLevelMapping + + for _, name := range f.Args() { + counter, ok := counters[name] + if !ok { + return cmd.ErrNotFound(name) + } + + mapping = append(mapping, types.PerformanceManagerCounterLevelMapping{ + CounterId: counter.Key, + AggregateLevel: int32(cmd.level), + PerDeviceLevel: int32(cmd.device), + }) + } + + _, err = methods.UpdateCounterLevelMapping(ctx, m.Client(), &types.UpdateCounterLevelMapping{ + This: m.Reference(), + CounterLevelMap: mapping, + }) + + return err +} diff --git a/vendor/github.com/vmware/govmomi/govc/metric/info.go b/vendor/github.com/vmware/govmomi/govc/metric/info.go new file mode 100644 index 00000000..c30cbfc3 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/metric/info.go @@ -0,0 +1,230 @@ +/* +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 metric + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "io" + "strings" + "text/tabwriter" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/vim25/types" +) + +type info struct { + *PerformanceFlag +} + +func init() { + cli.Register("metric.info", &info{}) +} + +func (cmd *info) Register(ctx context.Context, f *flag.FlagSet) { + cmd.PerformanceFlag, ctx = NewPerformanceFlag(ctx) + cmd.PerformanceFlag.Register(ctx, f) +} + +func (cmd *info) Usage() string { + return "PATH [NAME]..." +} + +func (cmd *info) Description() string { + return `Metric info for NAME. + +If PATH is a value other than '-', provider summary and instance list are included +for the given object type. + +If NAME is not specified, all available metrics for the given INTERVAL are listed. +An object PATH must be provided in this case. + +Examples: + govc metric.info vm/my-vm + govc metric.info -i 300 vm/my-vm + govc metric.info - cpu.usage.average + govc metric.info /dc1/host/cluster cpu.usage.average` +} + +func (cmd *info) Process(ctx context.Context) error { + if err := cmd.PerformanceFlag.Process(ctx); err != nil { + return err + } + return nil +} + +type EntityDetail struct { + Realtime bool + Historical bool + Instance []string +} + +type MetricInfo struct { + Counter *types.PerfCounterInfo + Enabled []string + PerDeviceEnabled []string + Detail *EntityDetail +} + +type infoResult struct { + cmd *info + Info []*MetricInfo +} + +func (r *infoResult) Write(w io.Writer) error { + tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0) + + for _, info := range r.Info { + counter := info.Counter + + fmt.Fprintf(tw, "Name:\t%s\n", counter.Name()) + fmt.Fprintf(tw, " Label:\t%s\n", counter.NameInfo.GetElementDescription().Label) + fmt.Fprintf(tw, " Summary:\t%s\n", counter.NameInfo.GetElementDescription().Summary) + fmt.Fprintf(tw, " Group:\t%s\n", counter.GroupInfo.GetElementDescription().Label) + fmt.Fprintf(tw, " Unit:\t%s\n", counter.UnitInfo.GetElementDescription().Label) + fmt.Fprintf(tw, " Rollup type:\t%s\n", counter.RollupType) + fmt.Fprintf(tw, " Stats type:\t%s\n", counter.StatsType) + fmt.Fprintf(tw, " Level:\t%d\n", counter.Level) + fmt.Fprintf(tw, " Intervals:\t%s\n", strings.Join(info.Enabled, ",")) + fmt.Fprintf(tw, " Per-device level:\t%d\n", counter.PerDeviceLevel) + fmt.Fprintf(tw, " Intervals:\t%s\n", strings.Join(info.PerDeviceEnabled, ",")) + + summary := info.Detail + if summary == nil { + continue + } + + fmt.Fprintf(tw, " Realtime:\t%t\n", summary.Realtime) + fmt.Fprintf(tw, " Historical:\t%t\n", summary.Historical) + fmt.Fprintf(tw, " Instances:\t%s\n", strings.Join(summary.Instance, ",")) + } + + return tw.Flush() +} + +func (r *infoResult) MarshalJSON() ([]byte, error) { + m := make(map[string]*MetricInfo) + + for _, info := range r.Info { + m[info.Counter.Name()] = info + } + + return json.Marshal(m) +} + +func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() == 0 { + return flag.ErrHelp + } + + names := f.Args()[1:] + + m, err := cmd.Manager(ctx) + if err != nil { + return err + } + + counters, err := m.CounterInfoByName(ctx) + if err != nil { + return err + } + + intervals, err := m.HistoricalInterval(ctx) + if err != nil { + return err + } + enabled := intervals.Enabled() + + var summary *types.PerfProviderSummary + var mids map[int32][]*types.PerfMetricId + + if f.Arg(0) == "-" { + if len(names) == 0 { + return flag.ErrHelp + } + } else { + objs, err := cmd.ManagedObjects(ctx, f.Args()[:1]) + if err != nil { + return err + } + + summary, err = m.ProviderSummary(ctx, objs[0]) + if err != nil { + return err + } + + all, err := m.AvailableMetric(ctx, objs[0], cmd.Interval(summary.RefreshRate)) + if err != nil { + return err + } + + mids = all.ByKey() + + if len(names) == 0 { + nc, _ := m.CounterInfoByKey(ctx) + + for i := range all { + id := &all[i] + if id.Instance != "" { + continue + } + + names = append(names, nc[id.CounterId].Name()) + } + } + } + + var metrics []*MetricInfo + + for _, name := range names { + counter, ok := counters[name] + if !ok { + return cmd.ErrNotFound(name) + } + + info := &MetricInfo{ + Counter: counter, + Enabled: enabled[counter.Level], + PerDeviceEnabled: enabled[counter.PerDeviceLevel], + } + + metrics = append(metrics, info) + + if summary == nil { + continue + } + + var instances []string + + for _, id := range mids[counter.Key] { + if id.Instance != "" { + instances = append(instances, id.Instance) + } + } + + info.Detail = &EntityDetail{ + Realtime: summary.CurrentSupported, + Historical: summary.SummarySupported, + Instance: instances, + } + + } + + return cmd.WriteResult(&infoResult{cmd, metrics}) +} diff --git a/vendor/github.com/vmware/govmomi/govc/metric/interval/change.go b/vendor/github.com/vmware/govmomi/govc/metric/interval/change.go new file mode 100644 index 00000000..ab034f9e --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/metric/interval/change.go @@ -0,0 +1,111 @@ +/* +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 interval + +import ( + "context" + "flag" + "fmt" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/govc/metric" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/types" +) + +type change struct { + *metric.PerformanceFlag + + enabled *bool + level int +} + +func init() { + cli.Register("metric.interval.change", &change{}) +} + +func (cmd *change) Register(ctx context.Context, f *flag.FlagSet) { + cmd.PerformanceFlag, ctx = metric.NewPerformanceFlag(ctx) + cmd.PerformanceFlag.Register(ctx, f) + + f.Var(flags.NewOptionalBool(&cmd.enabled), "enabled", "Enable or disable") + f.IntVar(&cmd.level, "level", 0, "Level") +} + +func (cmd *change) Description() string { + return `Change historical metric intervals. + +Examples: + govc metric.interval.change -i 300 -level 2 + govc metric.interval.change -i 86400 -enabled=false` +} + +func (cmd *change) Process(ctx context.Context) error { + if err := cmd.PerformanceFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *change) Run(ctx context.Context, f *flag.FlagSet) error { + m, err := cmd.Manager(ctx) + if err != nil { + return err + } + + intervals, err := m.HistoricalInterval(ctx) + if err != nil { + return err + } + + interval := cmd.Interval(0) + if interval == 0 { + return flag.ErrHelp + } + + var current *types.PerfInterval + + for _, i := range intervals { + if i.SamplingPeriod == interval { + current = &i + break + } + } + + if current == nil { + return fmt.Errorf("%d interval ID not found", interval) + } + + if cmd.level != 0 { + if cmd.level > 4 { + return flag.ErrHelp + } + current.Level = int32(cmd.level) + } + + if cmd.enabled != nil { + current.Enabled = *cmd.enabled + } + + _, err = methods.UpdatePerfInterval(ctx, m.Client(), &types.UpdatePerfInterval{ + This: m.Reference(), + Interval: *current, + }) + + return err +} diff --git a/vendor/github.com/vmware/govmomi/govc/metric/interval/info.go b/vendor/github.com/vmware/govmomi/govc/metric/interval/info.go new file mode 100644 index 00000000..01f66b9e --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/metric/interval/info.go @@ -0,0 +1,87 @@ +/* +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 interval + +import ( + "context" + "flag" + "fmt" + "text/tabwriter" + "time" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/metric" +) + +type info struct { + *metric.PerformanceFlag +} + +func init() { + cli.Register("metric.interval.info", &info{}) +} + +func (cmd *info) Register(ctx context.Context, f *flag.FlagSet) { + cmd.PerformanceFlag, ctx = metric.NewPerformanceFlag(ctx) + cmd.PerformanceFlag.Register(ctx, f) +} + +func (cmd *info) Description() string { + return `List historical metric intervals. + +Examples: + govc metric.interval.info + govc metric.interval.info -i 300` +} + +func (cmd *info) Process(ctx context.Context) error { + if err := cmd.PerformanceFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error { + m, err := cmd.Manager(ctx) + if err != nil { + return err + } + + intervals, err := m.HistoricalInterval(ctx) + if err != nil { + return err + } + + tw := tabwriter.NewWriter(cmd.Out, 2, 0, 2, ' ', 0) + cmd.Out = tw + + interval := cmd.Interval(0) + + for _, i := range intervals { + if interval != 0 && i.SamplingPeriod != interval { + continue + } + + fmt.Fprintf(cmd.Out, "ID:\t%d\n", i.SamplingPeriod) + fmt.Fprintf(cmd.Out, " Enabled:\t%t\n", i.Enabled) + fmt.Fprintf(cmd.Out, " Interval:\t%s\n", time.Duration(i.SamplingPeriod)*time.Second) + fmt.Fprintf(cmd.Out, " Name:\t%s\n", i.Name) + fmt.Fprintf(cmd.Out, " Level:\t%d\n", i.Level) + } + + return tw.Flush() +} diff --git a/vendor/github.com/vmware/govmomi/govc/metric/ls.go b/vendor/github.com/vmware/govmomi/govc/metric/ls.go new file mode 100644 index 00000000..69b30228 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/metric/ls.go @@ -0,0 +1,140 @@ +/* +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 metric + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "io" + "text/tabwriter" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/performance" + "github.com/vmware/govmomi/vim25/types" +) + +type ls struct { + *PerformanceFlag + + long bool +} + +func init() { + cli.Register("metric.ls", &ls{}) +} + +func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) { + cmd.PerformanceFlag, ctx = NewPerformanceFlag(ctx) + cmd.PerformanceFlag.Register(ctx, f) + + f.BoolVar(&cmd.long, "l", false, "Long listing format") +} + +func (cmd *ls) Usage() string { + return "PATH" +} + +func (cmd *ls) Description() string { + return `List available metrics for PATH. + +Examples: + govc metric.ls /dc1/host/cluster1 + govc metric.ls datastore/* + govc metric.ls vm/* | grep mem. | xargs govc metric.sample vm/*` +} + +func (cmd *ls) Process(ctx context.Context) error { + if err := cmd.PerformanceFlag.Process(ctx); err != nil { + return err + } + return nil +} + +type lsResult struct { + cmd *ls + counters map[int32]*types.PerfCounterInfo + performance.MetricList +} + +func (r *lsResult) Write(w io.Writer) error { + tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0) + + for _, id := range r.MetricList { + if id.Instance != "" { + continue + } + + info := r.counters[id.CounterId] + + if r.cmd.long { + fmt.Fprintf(w, "%s\t%s\n", info.Name(), + info.NameInfo.GetElementDescription().Label) + continue + } + + fmt.Fprintln(w, info.Name()) + } + + return tw.Flush() +} + +func (r *lsResult) MarshalJSON() ([]byte, error) { + m := make(map[string]*types.PerfCounterInfo) + + for _, id := range r.MetricList { + info := r.counters[id.CounterId] + + m[info.Name()] = info + } + + return json.Marshal(m) +} + +func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() != 1 { + return flag.ErrHelp + } + + objs, err := cmd.ManagedObjects(ctx, f.Args()) + if err != nil { + return err + } + + m, err := cmd.Manager(ctx) + if err != nil { + return err + } + + s, err := m.ProviderSummary(ctx, objs[0]) + if err != nil { + return err + } + + mids, err := m.AvailableMetric(ctx, objs[0], cmd.Interval(s.RefreshRate)) + if err != nil { + return err + } + + counters, err := m.CounterInfoByKey(ctx) + if err != nil { + return err + } + + return cmd.WriteResult(&lsResult{cmd, counters, mids}) +} diff --git a/vendor/github.com/vmware/govmomi/govc/metric/performance.go b/vendor/github.com/vmware/govmomi/govc/metric/performance.go new file mode 100644 index 00000000..893aeaa9 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/metric/performance.go @@ -0,0 +1,96 @@ +/* +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 metric + +import ( + "context" + "flag" + "fmt" + + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/performance" +) + +type PerformanceFlag struct { + *flags.DatacenterFlag + *flags.OutputFlag + + m *performance.Manager + + interval int +} + +func NewPerformanceFlag(ctx context.Context) (*PerformanceFlag, context.Context) { + f := &PerformanceFlag{} + f.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx) + f.OutputFlag, ctx = flags.NewOutputFlag(ctx) + return f, ctx +} + +func (f *PerformanceFlag) Register(ctx context.Context, fs *flag.FlagSet) { + f.DatacenterFlag.Register(ctx, fs) + f.OutputFlag.Register(ctx, fs) + + fs.IntVar(&f.interval, "i", 0, "Interval ID") +} + +func (f *PerformanceFlag) Process(ctx context.Context) error { + if err := f.DatacenterFlag.Process(ctx); err != nil { + return err + } + if err := f.OutputFlag.Process(ctx); err != nil { + return err + } + + return nil +} + +func (f *PerformanceFlag) Manager(ctx context.Context) (*performance.Manager, error) { + if f.m != nil { + return f.m, nil + } + + c, err := f.Client() + if err != nil { + return nil, err + } + + f.m = performance.NewManager(c) + + f.m.Sort = true + + return f.m, err +} + +func (f *PerformanceFlag) Interval(val int32) int32 { + interval := int32(f.interval) + + if interval == 0 { + if val == -1 { + // realtime not supported + return 300 + } + + return val + } + + return interval +} + +func (f *PerformanceFlag) ErrNotFound(name string) error { + return fmt.Errorf("counter %q not found", name) +} diff --git a/vendor/github.com/vmware/govmomi/govc/metric/reset.go b/vendor/github.com/vmware/govmomi/govc/metric/reset.go new file mode 100644 index 00000000..a73a220a --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/metric/reset.go @@ -0,0 +1,91 @@ +/* +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 metric + +import ( + "context" + "flag" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/types" +) + +type reset struct { + *PerformanceFlag +} + +func init() { + cli.Register("metric.reset", &reset{}) +} + +func (cmd *reset) Register(ctx context.Context, f *flag.FlagSet) { + cmd.PerformanceFlag, ctx = NewPerformanceFlag(ctx) + cmd.PerformanceFlag.Register(ctx, f) +} + +func (cmd *reset) Usage() string { + return "NAME..." +} + +func (cmd *reset) Description() string { + return `Reset counter NAME to the default level of data collection. + +Examples: + govc metric.reset net.bytesRx.average net.bytesTx.average` +} + +func (cmd *reset) Process(ctx context.Context) error { + if err := cmd.PerformanceFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *reset) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() == 0 { + return flag.ErrHelp + } + + m, err := cmd.Manager(ctx) + if err != nil { + return err + } + + counters, err := m.CounterInfoByName(ctx) + if err != nil { + return err + } + + var ids []int32 + + for _, name := range f.Args() { + counter, ok := counters[name] + if !ok { + return cmd.ErrNotFound(name) + } + + ids = append(ids, counter.Key) + } + + _, err = methods.ResetCounterLevelMapping(ctx, m.Client(), &types.ResetCounterLevelMapping{ + This: m.Reference(), + Counters: ids, + }) + + return err +} diff --git a/vendor/github.com/vmware/govmomi/govc/metric/sample.go b/vendor/github.com/vmware/govmomi/govc/metric/sample.go new file mode 100644 index 00000000..a85ca20f --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/metric/sample.go @@ -0,0 +1,330 @@ +/* +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 metric + +import ( + "context" + "crypto/md5" + "flag" + "fmt" + "io" + "os" + "os/exec" + "path" + "strings" + "text/tabwriter" + "time" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/performance" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +type sample struct { + *PerformanceFlag + + d int + n int + t bool + plot string + instance string +} + +func init() { + cli.Register("metric.sample", &sample{}) +} + +func (cmd *sample) Register(ctx context.Context, f *flag.FlagSet) { + cmd.PerformanceFlag, ctx = NewPerformanceFlag(ctx) + cmd.PerformanceFlag.Register(ctx, f) + + f.IntVar(&cmd.d, "d", 30, "Limit object display name to D chars") + f.IntVar(&cmd.n, "n", 6, "Max number of samples") + f.StringVar(&cmd.plot, "plot", "", "Plot data using gnuplot") + f.BoolVar(&cmd.t, "t", false, "Include sample times") + f.StringVar(&cmd.instance, "instance", "*", "Instance") +} + +func (cmd *sample) Usage() string { + return "PATH... NAME..." +} + +func (cmd *sample) Description() string { + return `Sample for object PATH of metric NAME. + +Interval ID defaults to 20 (realtime) if supported, otherwise 300 (5m interval). + +By default, INSTANCE '*' samples all instances and the aggregate counter. +An INSTANCE value of '-' will only sample the aggregate counter. +An INSTANCE value other than '*' or '-' will only sample the given instance counter. + +If PLOT value is set to '-', output a gnuplot script. If non-empty with another +value, PLOT will pipe the script to gnuplot for you. The value is also used to set +the gnuplot 'terminal' variable, unless the value is that of the DISPLAY env var. +Only 1 metric NAME can be specified when the PLOT flag is set. + +Examples: + govc metric.sample host/cluster1/* cpu.usage.average + govc metric.sample -plot .png host/cluster1/* cpu.usage.average | xargs open + govc metric.sample vm/* net.bytesTx.average net.bytesTx.average + govc metric.sample -instance vmnic0 vm/* net.bytesTx.average + govc metric.sample -instance - vm/* net.bytesTx.average` +} + +func (cmd *sample) Process(ctx context.Context) error { + if err := cmd.PerformanceFlag.Process(ctx); err != nil { + return err + } + return nil +} + +type sampleResult struct { + cmd *sample + m *performance.Manager + counters map[string]*types.PerfCounterInfo + Sample []performance.EntityMetric +} + +func (r *sampleResult) name(e types.ManagedObjectReference) string { + var me mo.ManagedEntity + _ = r.m.Properties(context.Background(), e, []string{"name"}, &me) + + name := me.Name + + if r.cmd.d > 0 && len(name) > r.cmd.d { + return name[:r.cmd.d] + "*" + } + + return name +} + +func sampleInfoTimes(m *performance.EntityMetric) []string { + vals := make([]string, len(m.SampleInfo)) + + for i := range m.SampleInfo { + vals[i] = m.SampleInfo[i].Timestamp.Format(time.RFC3339) + } + + return vals +} + +func (r *sampleResult) Plot(w io.Writer) error { + if len(r.Sample) == 0 { + return nil + } + + if r.cmd.plot != "-" { + cmd := exec.Command("gnuplot", "-persist") + cmd.Stdout = w + cmd.Stderr = os.Stderr + stdin, err := cmd.StdinPipe() + if err != nil { + return err + } + + if err = cmd.Start(); err != nil { + return err + } + + w = stdin + defer func() { + _ = stdin.Close() + _ = cmd.Wait() + }() + } + + counter := r.counters[r.Sample[0].Value[0].Name] + unit := counter.UnitInfo.GetElementDescription() + + fmt.Fprintf(w, "set title %q\n", counter.Name()) + fmt.Fprintf(w, "set ylabel %q\n", unit.Label) + fmt.Fprintf(w, "set xlabel %q\n", "Time") + fmt.Fprintf(w, "set xdata %s\n", "time") + fmt.Fprintf(w, "set format x %q\n", "%H:%M") + fmt.Fprintf(w, "set timefmt %q\n", "%Y-%m-%dT%H:%M:%SZ") + + ext := path.Ext(r.cmd.plot) + if ext != "" { + // If a file name is given, use the extension as terminal type. + // If just an ext is given, use the entities and counter as the file name. + file := r.cmd.plot + name := r.cmd.plot[:len(r.cmd.plot)-len(ext)] + r.cmd.plot = ext[1:] + + if name == "" { + h := md5.New() + + for i := range r.Sample { + _, _ = io.WriteString(h, r.Sample[i].Entity.String()) + } + _, _ = io.WriteString(h, counter.Name()) + + file = fmt.Sprintf("govc-plot-%x%s", h.Sum(nil), ext) + } + + fmt.Fprintf(w, "set output %q\n", file) + + defer func() { + fmt.Fprintln(r.cmd.Out, file) + }() + } + + switch r.cmd.plot { + case "-", os.Getenv("DISPLAY"): + default: + fmt.Fprintf(w, "set terminal %s\n", r.cmd.plot) + } + + if unit.Key == string(types.PerformanceManagerUnitPercent) { + fmt.Fprintln(w, "set yrange [0:100]") + } + + fmt.Fprintln(w) + + var set []string + + for i := range r.Sample { + name := r.name(r.Sample[i].Entity) + name = strings.Replace(name, "_", "*", -1) // underscore is some gnuplot markup? + set = append(set, fmt.Sprintf("'-' using 1:2 title '%s' with lines", name)) + } + + fmt.Fprintf(w, "plot %s\n", strings.Join(set, ", ")) + + for i := range r.Sample { + times := sampleInfoTimes(&r.Sample[i]) + + for _, value := range r.Sample[i].Value { + for j := range value.Value { + fmt.Fprintf(w, "%s %s\n", times[j], value.Format(value.Value[j])) + } + } + + fmt.Fprintln(w, "e") + } + + return nil +} + +func (r *sampleResult) Write(w io.Writer) error { + if r.cmd.plot != "" { + return r.Plot(w) + } + + cmd := r.cmd + tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0) + + for i := range r.Sample { + metric := r.Sample[i] + name := r.name(metric.Entity) + t := "" + if cmd.t { + t = metric.SampleInfoCSV() + } + + for _, v := range metric.Value { + counter := r.counters[v.Name] + units := counter.UnitInfo.GetElementDescription().Label + + instance := v.Instance + if instance == "" { + instance = "-" + } + + fmt.Fprintf(tw, "%s\t%s\t%s\t%v\t%s\t%s\n", + name, instance, v.Name, t, v.ValueCSV(), units) + } + } + + return tw.Flush() +} + +func (cmd *sample) Run(ctx context.Context, f *flag.FlagSet) error { + m, err := cmd.Manager(ctx) + if err != nil { + return err + } + + var paths []string + var names []string + + byName, err := m.CounterInfoByName(ctx) + if err != nil { + return err + } + + for _, arg := range f.Args() { + if _, ok := byName[arg]; ok { + names = append(names, arg) + } else { + paths = append(paths, arg) + } + } + + if len(paths) == 0 || len(names) == 0 { + return flag.ErrHelp + } + + if cmd.plot != "" { + if len(names) > 1 { + return flag.ErrHelp + } + + if cmd.instance == "*" { + cmd.instance = "" + } + } + + objs, err := cmd.ManagedObjects(ctx, paths) + if err != nil { + return err + } + + s, err := m.ProviderSummary(ctx, objs[0]) + if err != nil { + return err + } + + if cmd.instance == "-" { + cmd.instance = "" + } + + spec := types.PerfQuerySpec{ + Format: string(types.PerfFormatNormal), + MaxSample: int32(cmd.n), + MetricId: []types.PerfMetricId{{Instance: cmd.instance}}, + IntervalId: cmd.Interval(s.RefreshRate), + } + + sample, err := m.SampleByName(ctx, spec, names, objs) + if err != nil { + return err + } + + result, err := m.ToMetricSeries(ctx, sample) + if err != nil { + return err + } + + counters, err := m.CounterInfoByName(ctx) + if err != nil { + return err + } + + return cmd.WriteResult(&sampleResult{cmd, m, counters, result}) +} diff --git a/vendor/github.com/vmware/govmomi/govc/object/collect.go b/vendor/github.com/vmware/govmomi/govc/object/collect.go index 48b22909..3f696695 100644 --- a/vendor/github.com/vmware/govmomi/govc/object/collect.go +++ b/vendor/github.com/vmware/govmomi/govc/object/collect.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2016 VMware, Inc. All Rights Reserved. +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. @@ -29,6 +29,7 @@ import ( "github.com/vmware/govmomi/govc/cli" "github.com/vmware/govmomi/govc/flags" "github.com/vmware/govmomi/property" + "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/types" ) @@ -39,6 +40,10 @@ type collect struct { single bool simple bool n int + kind kinds + + filter property.Filter + obj string } func init() { @@ -51,6 +56,7 @@ func (cmd *collect) Register(ctx context.Context, f *flag.FlagSet) { f.BoolVar(&cmd.simple, "s", false, "Output property value only") f.IntVar(&cmd.n, "n", 0, "Wait for N property updates") + f.Var(&cmd.kind, "type", "Resource type. If specified, MOID is used for a container view root") } func (cmd *collect) Usage() string { @@ -61,14 +67,20 @@ func (cmd *collect) Description() string { return `Collect managed object properties. MOID can be an inventory path or ManagedObjectReference. -MOID defaults to '-', an alias for 'ServiceInstance:ServiceInstance'. +MOID defaults to '-', an alias for 'ServiceInstance:ServiceInstance' or the root folder if a '-type' flag is given. + +If a '-type' flag is given, properties are collected using a ContainerView object where MOID is the root of the view. -By default only the current property value(s) are collected. Use the '-n' flag to wait for updates. +By default only the current property value(s) are collected. To wait for updates, use the '-n' flag or +specify a property filter. A property filter can be specified by prefixing the property name with a '-', +followed by the value to match. Examples: govc object.collect - content govc object.collect -s HostSystem:ha-host hardware.systemInfo.uuid govc object.collect -s /ha-datacenter/vm/foo overallStatus + govc object.collect -s /ha-datacenter/vm/foo -guest.guestOperationsReady true # property filter + govc object.collect -type m / name runtime.powerState # collect properties for multiple objects govc object.collect -json -n=-1 EventManager:ha-eventmgr latestEvent | jq . govc object.collect -json -s $(govc object.collect -s - content.perfManager) description.counterType | jq .` } @@ -83,12 +95,16 @@ func (cmd *collect) Process(ctx context.Context) error { var stringer = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() type change struct { - cmd *collect - PropertyChange []types.PropertyChange + cmd *collect + Update types.ObjectUpdate } func (pc *change) MarshalJSON() ([]byte, error) { - return json.Marshal(pc.PropertyChange) + if len(pc.cmd.kind) == 0 { + return json.Marshal(pc.Update.ChangeSet) + } + + return json.Marshal(pc.Update) } func (pc *change) output(name string, rval reflect.Value, rtype reflect.Type) { @@ -145,14 +161,33 @@ func (pc *change) output(name string, rval reflect.Value, rtype reflect.Type) { return } + if pc.cmd.obj != "" { + fmt.Fprintf(pc.cmd.Out, "%s\t", pc.cmd.obj) + } + fmt.Fprintf(pc.cmd.Out, "%s\t%s\t%s\n", name, rtype, s) } +func (pc *change) writeStruct(name string, rval reflect.Value, rtype reflect.Type) { + for i := 0; i < rval.NumField(); i++ { + fval := rval.Field(i) + field := rtype.Field(i) + + if field.Anonymous { + pc.writeStruct(name, fval, fval.Type()) + continue + } + + fname := fmt.Sprintf("%s.%s%s", name, strings.ToLower(field.Name[:1]), field.Name[1:]) + pc.output(fname, fval, field.Type) + } +} + func (pc *change) Write(w io.Writer) error { tw := tabwriter.NewWriter(pc.cmd.Out, 4, 0, 2, ' ', 0) pc.cmd.Out = tw - for _, c := range pc.PropertyChange { + for _, c := range pc.Update.ChangeSet { if c.Val == nil { // type is unknown in this case, as xsi:type was not provided - just skip for now continue @@ -166,18 +201,12 @@ func (pc *change) Write(w io.Writer) error { rtype = rval.Type() } - if pc.cmd.single && rtype.Kind() == reflect.Struct && !rtype.Implements(stringer) { - for i := 0; i < rval.NumField(); i++ { - fval := rval.Field(i) - field := rtype.Field(i) - - if field.Anonymous { - continue - } + if len(pc.cmd.kind) != 0 { + pc.cmd.obj = pc.Update.Obj.String() + } - fname := fmt.Sprintf("%s.%s%s", c.Name, strings.ToLower(field.Name[:1]), field.Name[1:]) - pc.output(fname, fval, field.Type) - } + if pc.cmd.single && rtype.Kind() == reflect.Struct && !rtype.Implements(stringer) { + pc.writeStruct(c.Name, rval, rtype) continue } @@ -187,6 +216,33 @@ func (pc *change) Write(w io.Writer) error { return tw.Flush() } +func (cmd *collect) match(update types.ObjectUpdate) bool { + if len(cmd.filter) == 0 { + return false + } + + for _, c := range update.ChangeSet { + if cmd.filter.MatchProperty(types.DynamicProperty{Name: c.Name, Val: c.Val}) { + return true + } + } + + return false +} + +func (cmd *collect) toFilter(f *flag.FlagSet, props []string) ([]string, error) { + // TODO: Only supporting 1 filter prop for now. More than one would require some + // accounting / accumulating of multiple updates. And need to consider objects + // then enter/leave a container view. + if len(props) != 2 || !strings.HasPrefix(props[0], "-") { + return props, nil + } + + cmd.filter = property.Filter{props[0][1:]: props[1]} + + return cmd.filter.Keys(), nil +} + func (cmd *collect) Run(ctx context.Context, f *flag.FlagSet) error { client, err := cmd.Client() if err != nil { @@ -201,6 +257,10 @@ func (cmd *collect) Run(ctx context.Context, f *flag.FlagSet) error { ref := methods.ServiceInstance arg := f.Arg(0) + if len(cmd.kind) != 0 { + ref = client.ServiceContent.RootFolder + } + switch arg { case "", "-": default: @@ -222,6 +282,7 @@ func (cmd *collect) Run(ctx context.Context, f *flag.FlagSet) error { } p := property.DefaultCollector(client) + filter := new(property.WaitFilter) var props []string if f.NArg() > 1 { @@ -229,8 +290,63 @@ func (cmd *collect) Run(ctx context.Context, f *flag.FlagSet) error { cmd.single = len(props) == 1 } - return property.Wait(ctx, p, ref, props, func(pc []types.PropertyChange) bool { - _ = cmd.WriteResult(&change{cmd, pc}) + props, err = cmd.toFilter(f, props) + if err != nil { + return err + } + + if len(cmd.kind) == 0 { + filter.Add(ref, ref.Type, props) + } else { + m := view.NewManager(client) + + v, err := m.CreateContainerView(ctx, ref, cmd.kind, true) + if err != nil { + return err + } + + defer v.Destroy(ctx) + + for _, kind := range cmd.kind { + filter.Add(v.Reference(), kind, props, v.TraversalSpec()) + } + } + + entered := false + hasFilter := len(cmd.filter) != 0 + + return property.WaitForUpdates(ctx, p, filter, func(updates []types.ObjectUpdate) bool { + matches := 0 + + for _, update := range updates { + if entered && update.Kind == types.ObjectUpdateKindEnter { + // on the first update we only get kind "enter" + // if a new object is added, the next update with have both "enter" and "modify". + continue + } + + c := &change{cmd, update} + + if hasFilter { + if cmd.match(update) { + matches++ + } else { + continue + } + } + + _ = cmd.WriteResult(c) + } + + entered = true + + if hasFilter { + if matches > 0 { + return true + } + + return false + } cmd.n-- diff --git a/vendor/github.com/vmware/govmomi/govc/object/find.go b/vendor/github.com/vmware/govmomi/govc/object/find.go new file mode 100644 index 00000000..5b924566 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/object/find.go @@ -0,0 +1,324 @@ +/* +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 ( + "bytes" + "context" + "flag" + "fmt" + "strings" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/property" + "github.com/vmware/govmomi/view" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/types" +) + +type find struct { + *flags.DatacenterFlag + + ref bool + kind kinds + name string + maxdepth int +} + +var alias = []struct { + name string + kind string +}{ + {"a", "VirtualApp"}, + {"c", "ClusterComputeResource"}, + {"d", "Datacenter"}, + {"f", "Folder"}, + {"g", "DistributedVirtualPortgroup"}, + {"h", "HostSystem"}, + {"m", "VirtualMachine"}, + {"n", "Network"}, + {"o", "OpaqueNetwork"}, + {"p", "ResourcePool"}, + {"r", "ComputeResource"}, + {"s", "Datastore"}, + {"w", "DistributedVirtualSwitch"}, +} + +func aliasHelp() string { + var help bytes.Buffer + + for _, a := range alias { + fmt.Fprintf(&help, " %s %s\n", a.name, a.kind) + } + + return help.String() +} + +type kinds []string + +func (e *kinds) String() string { + return fmt.Sprint(*e) +} + +func (e *kinds) Set(value string) error { + *e = append(*e, e.alias(value)) + return nil +} + +func (e *kinds) alias(value string) string { + if len(value) != 1 { + return value + } + + for _, a := range alias { + if a.name == value { + return a.kind + } + } + + return value +} + +func (e *kinds) wanted(kind string) bool { + if len(*e) == 0 { + return true + } + + for _, k := range *e { + if kind == k { + return true + } + } + + return false +} + +func init() { + cli.Register("find", &find{}) +} + +func (cmd *find) Register(ctx context.Context, f *flag.FlagSet) { + cmd.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx) + cmd.DatacenterFlag.Register(ctx, f) + + f.Var(&cmd.kind, "type", "Resource type") + f.StringVar(&cmd.name, "name", "*", "Resource name") + f.IntVar(&cmd.maxdepth, "maxdepth", -1, "Max depth") + f.BoolVar(&cmd.ref, "i", false, "Print the managed object reference") +} + +func (cmd *find) Usage() string { + return "[ROOT] [KEY VAL]..." +} + +func (cmd *find) Description() string { + atable := aliasHelp() + + return fmt.Sprintf(`Find managed objects. + +ROOT can be an inventory path or ManagedObjectReference. +ROOT defaults to '.', an alias for the root folder or DC if set. + +Optional KEY VAL pairs can be used to filter results against object instance properties. + +The '-type' flag value can be a managed entity type or one of the following aliases: + +%s +Examples: + govc find + govc find /dc1 -type c + govc find vm -name my-vm-* + govc find . -type n + govc find . -type m -runtime.powerState poweredOn + govc find . -type m -datastore $(govc find -i datastore -name vsanDatastore) + govc find . -type s -summary.type vsan + govc find . -type h -hardware.cpuInfo.numCpuCores 16`, atable) +} + +func (cmd *find) Process(ctx context.Context) error { + if err := cmd.DatacenterFlag.Process(ctx); err != nil { + return err + } + return nil +} + +// rootMatch returns true if the root object path should be printed +func (cmd *find) rootMatch(ctx context.Context, root object.Reference, client *vim25.Client, filter property.Filter) bool { + ref := root.Reference() + + if !cmd.kind.wanted(ref.Type) { + return false + } + + if len(filter) == 1 && filter["name"] == "*" { + return true + } + + var content []types.ObjectContent + + pc := property.DefaultCollector(client) + _ = pc.RetrieveWithFilter(ctx, []types.ManagedObjectReference{ref}, filter.Keys(), &content, filter) + + return content != nil +} + +func (cmd *find) Run(ctx context.Context, f *flag.FlagSet) error { + client, err := cmd.Client() + if err != nil { + return err + } + + finder, err := cmd.Finder() + if err != nil { + return err + } + + root := client.ServiceContent.RootFolder + rootPath := "/" + + arg := f.Arg(0) + props := f.Args() + + if len(props) > 0 { + if strings.HasPrefix(arg, "-") { + arg = "." + } else { + props = props[1:] + } + } + + if len(props)%2 != 0 { + return flag.ErrHelp + } + + switch arg { + case rootPath: + case "", ".": + dc, _ := cmd.DatacenterIfSpecified() + if dc == nil { + arg = rootPath + } else { + arg = "." + root = dc.Reference() + rootPath = dc.InventoryPath + } + default: + l, ferr := finder.ManagedObjectList(ctx, arg) + if ferr != nil { + return err + } + + switch len(l) { + case 0: + return fmt.Errorf("%s not found", arg) + case 1: + root = l[0].Object.Reference() + rootPath = l[0].Path + default: + return flag.ErrHelp + } + } + + filter := property.Filter{} + + if len(props)%2 != 0 { + return flag.ErrHelp + } + + for i := 0; i < len(props); i++ { + key := props[i] + if !strings.HasPrefix(key, "-") { + return flag.ErrHelp + } + + key = key[1:] + i++ + val := props[i] + + if xf := f.Lookup(key); xf != nil { + // Support use of -flag following the ROOT arg (flag package does not do this) + if err = xf.Value.Set(val); err != nil { + return err + } + } else { + filter[key] = val + } + } + + filter["name"] = cmd.name + + printPath := func(o types.ManagedObjectReference, p string) { + if cmd.ref { + fmt.Fprintln(cmd.Out, o) + return + } + + path := strings.Replace(p, rootPath, arg, 1) + fmt.Fprintln(cmd.Out, path) + } + + recurse := false + + switch cmd.maxdepth { + case -1: + recurse = true + case 0: + case 1: + default: + return flag.ErrHelp // TODO: ? + } + + if cmd.rootMatch(ctx, root, client, filter) { + printPath(root, arg) + } + + if cmd.maxdepth == 0 { + return nil + } + + m := view.NewManager(client) + + v, err := m.CreateContainerView(ctx, root, cmd.kind, recurse) + if err != nil { + return err + } + + defer v.Destroy(ctx) + + objs, err := v.Find(ctx, cmd.kind, filter) + if err != nil { + return err + } + + for _, o := range objs { + var path string + + if !cmd.ref { + e, err := finder.Element(ctx, o) + if err != nil { + return err + } + path = e.Path + } + + printPath(o, path) + } + + return nil +} diff --git a/vendor/github.com/vmware/govmomi/govc/object/method.go b/vendor/github.com/vmware/govmomi/govc/object/method.go new file mode 100644 index 00000000..3cc24e98 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/object/method.go @@ -0,0 +1,104 @@ +/* +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" + "flag" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/object" +) + +type method struct { + *flags.DatacenterFlag + + name string + reason string + source string + enable bool +} + +func init() { + cli.Register("object.method", &method{}) +} + +func (cmd *method) Register(ctx context.Context, f *flag.FlagSet) { + cmd.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx) + cmd.DatacenterFlag.Register(ctx, f) + + f.StringVar(&cmd.name, "name", "", "Method name") + f.StringVar(&cmd.reason, "reason", "", "Reason for disabling method") + f.StringVar(&cmd.source, "source", "govc", "Source ID") + f.BoolVar(&cmd.enable, "enable", true, "Enable method") +} + +func (cmd *method) Usage() string { + return "PATH..." +} + +func (cmd *method) Description() string { + return `Enable or disable methods for managed objects. + +Examples: + govc object.method -name Destroy_Task -enable=false /dc1/vm/foo + govc object.collect /dc1/vm/foo disabledMethod | grep --color Destroy_Task + govc object.method -name Destroy_Task -enable /dc1/vm/foo` +} + +func (cmd *method) Process(ctx context.Context) error { + if err := cmd.DatacenterFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *method) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() == 0 { + return flag.ErrHelp + } + + if cmd.name == "" { + return flag.ErrHelp + } + + c, err := cmd.Client() + if err != nil { + return err + } + + objs, err := cmd.ManagedObjects(ctx, f.Args()) + if err != nil { + return err + } + + m := object.NewAuthorizationManager(c) + + if cmd.enable { + return m.EnableMethods(ctx, objs, []string{cmd.name}, cmd.source) + } + + method := []object.DisabledMethodRequest{ + { + Method: cmd.name, + Reason: cmd.reason, + }, + } + + return m.DisableMethods(ctx, objs, method, cmd.source) +} diff --git a/vendor/github.com/vmware/govmomi/govc/option/ls.go b/vendor/github.com/vmware/govmomi/govc/option/ls.go new file mode 100644 index 00000000..4facb0c7 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/option/ls.go @@ -0,0 +1,121 @@ +/* +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 option + +import ( + "context" + "flag" + "fmt" + "io" + "os" + "text/tabwriter" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +type List struct { + *flags.ClientFlag + *flags.OutputFlag +} + +func init() { + cli.Register("option.ls", &List{}) +} + +func (cmd *List) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) + + cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx) + cmd.OutputFlag.Register(ctx, f) +} + +func (cmd *List) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + if err := cmd.OutputFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *List) Usage() string { + return "[NAME]" +} + +var ListDescription = `List option with the given NAME. + +If NAME ends with a dot, all options for that subtree are listed.` + +func (cmd *List) Description() string { + return ListDescription + ` + +Examples: + govc option.ls + govc option.ls config.vpxd.sso. + govc option.ls config.vpxd.sso.sts.uri` +} + +func (cmd *List) Query(ctx context.Context, f *flag.FlagSet, m *object.OptionManager) error { + var err error + var opts []types.BaseOptionValue + + if f.NArg() > 1 { + return flag.ErrHelp + } + + if f.NArg() == 1 { + opts, err = m.Query(ctx, f.Arg(0)) + } else { + var om mo.OptionManager + err = m.Properties(ctx, m.Reference(), []string{"setting"}, &om) + opts = om.Setting + } + + if err != nil { + return err + } + + return cmd.WriteResult(optionResult(opts)) +} + +func (cmd *List) Run(ctx context.Context, f *flag.FlagSet) error { + c, err := cmd.Client() + if err != nil { + return err + } + + m := object.NewOptionManager(c, *c.ServiceContent.Setting) + + return cmd.Query(ctx, f, m) +} + +type optionResult []types.BaseOptionValue + +func (r optionResult) Write(w io.Writer) error { + tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0) + for _, opt := range r { + o := opt.GetOptionValue() + fmt.Fprintf(tw, "%s:\t%v\n", o.Key, o.Value) + } + return tw.Flush() +} diff --git a/vendor/github.com/vmware/govmomi/govc/option/set.go b/vendor/github.com/vmware/govmomi/govc/option/set.go new file mode 100644 index 00000000..09d18f44 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/option/set.go @@ -0,0 +1,120 @@ +/* +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 option + +import ( + "context" + "flag" + "fmt" + "strconv" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/types" +) + +type Set struct { + *flags.ClientFlag +} + +func init() { + cli.Register("option.set", &Set{}) +} + +func (cmd *Set) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) +} + +func (cmd *Set) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *Set) Usage() string { + return "NAME VALUE" +} + +var SetDescription = `Set option NAME to VALUE.` + +func (cmd *Set) Description() string { + return SetDescription + ` + +Examples: + govc option.set log.level info + govc option.set logger.Vsan verbose` +} + +func (cmd *Set) Update(ctx context.Context, f *flag.FlagSet, m *object.OptionManager) error { + if f.NArg() != 2 { + return flag.ErrHelp + } + + name := f.Arg(0) + opts, err := m.Query(ctx, name) + if err != nil { + return err + } + + if len(opts) != 1 { + return flag.ErrHelp + } + + val := f.Arg(1) + var set types.AnyType + + switch x := opts[0].GetOptionValue().Value.(type) { + case string: + set = val + case bool: + set, err = strconv.ParseBool(val) + if err != nil { + return err + } + case int32: + s, err := strconv.ParseInt(val, 10, 32) + if err != nil { + return err + } + set = s + case int64: + set, err = strconv.ParseInt(val, 10, 64) + if err != nil { + return err + } + default: + return fmt.Errorf("type %T conversion not supported", x) + } + + opts[0].GetOptionValue().Value = set + + return m.Update(ctx, opts) +} + +func (cmd *Set) Run(ctx context.Context, f *flag.FlagSet) error { + c, err := cmd.Client() + if err != nil { + return err + } + + m := object.NewOptionManager(c, *c.ServiceContent.Setting) + + return cmd.Update(ctx, f, m) +} diff --git a/vendor/github.com/vmware/govmomi/govc/role/update.go b/vendor/github.com/vmware/govmomi/govc/role/update.go index 3937626a..ad059ad0 100644 --- a/vendor/github.com/vmware/govmomi/govc/role/update.go +++ b/vendor/github.com/vmware/govmomi/govc/role/update.go @@ -42,7 +42,7 @@ func (cmd *update) Register(ctx context.Context, f *flag.FlagSet) { f.StringVar(&cmd.name, "name", "", "Change role name") f.BoolVar(&cmd.remove, "r", false, "Remove given PRIVILEGE(s)") - f.BoolVar(&cmd.add, "a", false, "Remove given PRIVILEGE(s)") + f.BoolVar(&cmd.add, "a", false, "Add given PRIVILEGE(s)") } func (cmd *update) Process(ctx context.Context) error { diff --git a/vendor/github.com/vmware/govmomi/govc/task/cancel.go b/vendor/github.com/vmware/govmomi/govc/task/cancel.go new file mode 100644 index 00000000..77187186 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/task/cancel.go @@ -0,0 +1,81 @@ +/* +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 task + +import ( + "context" + "flag" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/types" +) + +type cancel struct { + *flags.ClientFlag +} + +func init() { + cli.Register("task.cancel", &cancel{}) +} + +func (cmd *cancel) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) +} + +func (cmd *cancel) Description() string { + return `Cancel tasks. + +Examples: + govc task.cancel task-759` +} + +func (cmd *cancel) Usage() string { + return "ID..." +} + +func (cmd *cancel) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *cancel) Run(ctx context.Context, f *flag.FlagSet) error { + c, err := cmd.Client() + if err != nil { + return err + } + + for _, id := range f.Args() { + var ref types.ManagedObjectReference + + if !ref.FromString(id) { + ref.Type = "Task" + ref.Value = id + } + + err = object.NewTask(c, ref).Cancel(ctx) + if err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/vmware/govmomi/govc/task/recent.go b/vendor/github.com/vmware/govmomi/govc/task/recent.go new file mode 100644 index 00000000..f5bd92fa --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/task/recent.go @@ -0,0 +1,179 @@ +/* +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 task + +import ( + "context" + "flag" + "fmt" + "strings" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/view" + "github.com/vmware/govmomi/vim25/types" +) + +type recent struct { + *flags.DatacenterFlag + + max int + follow bool +} + +func init() { + cli.Register("tasks", &recent{}) +} + +func (cmd *recent) Register(ctx context.Context, f *flag.FlagSet) { + cmd.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx) + cmd.DatacenterFlag.Register(ctx, f) + + f.IntVar(&cmd.max, "n", 25, "Output the last N tasks") + f.BoolVar(&cmd.follow, "f", false, "Follow recent task updates") +} + +func (cmd *recent) Description() string { + return `Display info for recent tasks. + +When a task has completed, the result column includes the task duration on success or +error message on failure. If a task is still in progress, the result column displays +the completion percentage and the task ID. The task ID can be used as an argument to +the 'task.cancel' command. + +By default, all recent tasks are included (via TaskManager), but can be limited by PATH +to a specific inventory object. + +Examples: + govc tasks + govc tasks -f + govc tasks -f /dc1/host/cluster1` +} + +func (cmd *recent) Usage() string { + return "[PATH]" +} + +func (cmd *recent) Process(ctx context.Context) error { + if err := cmd.DatacenterFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func chop(s string) string { + if len(s) < 30 { + return s + } + + return s[:29] + "*" +} + +func (cmd *recent) Run(ctx context.Context, f *flag.FlagSet) error { + c, err := cmd.Client() + if err != nil { + return err + } + + m := c.ServiceContent.TaskManager + + watch := *m + + if f.NArg() == 1 { + refs, merr := cmd.ManagedObjects(ctx, f.Args()) + if merr != nil { + return merr + } + watch = refs[0] + } + + v, err := view.NewManager(c).CreateTaskView(ctx, &watch) + if err != nil { + return nil + } + + defer v.Destroy(context.Background()) + + v.Follow = cmd.follow + + stamp := "15:04:05" + tmpl := "%-30s %-30s %13s %9s %9s %9s %s\n" + fmt.Fprintf(cmd.Out, tmpl, "Task", "Target", "Initiator", "Queued", "Started", "Completed", "Result") + + var last string + updated := false + + return v.Collect(ctx, func(tasks []types.TaskInfo) { + if !updated && len(tasks) > cmd.max { + tasks = tasks[len(tasks)-cmd.max:] + } + updated = true + + for _, info := range tasks { + var user string + + switch x := info.Reason.(type) { + case *types.TaskReasonUser: + user = x.UserName + } + + if info.EntityName == "" || user == "" { + continue + } + + ruser := strings.SplitN(user, "\\", 2) + if len(ruser) == 2 { + user = ruser[1] // discard domain + } + + queued := info.QueueTime.Format(stamp) + start := "-" + end := start + + if info.StartTime != nil { + start = info.StartTime.Format(stamp) + } + + result := fmt.Sprintf("%2d%% %s", info.Progress, info.Task) + + if info.CompleteTime != nil { + if info.State == types.TaskInfoStateError { + result = info.Error.LocalizedMessage + } else { + result = fmt.Sprintf("%s (%s)", info.State, info.CompleteTime.Sub(*info.StartTime).String()) + } + + end = info.CompleteTime.Format(stamp) + } + + name := strings.TrimSuffix(info.Name, "_Task") + switch name { + case "Destroy", "Rename": + name = info.Entity.Type + "." + name + } + + item := fmt.Sprintf(tmpl, name, chop(info.EntityName), user, queued, start, end, result) + + if item == last { + continue // task info was updated, but the fields we display were not + } + last = item + + fmt.Fprint(cmd.Out, item) + } + }) +} diff --git a/vendor/github.com/vmware/govmomi/govc/vm/change.go b/vendor/github.com/vmware/govmomi/govc/vm/change.go index bbfcecf4..af00b36c 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/change.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/change.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2015 VMware, Inc. All Rights Reserved. +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. @@ -37,8 +37,6 @@ func (e *extraConfig) Set(v string) error { r := strings.SplitN(v, "=", 2) if len(r) < 2 { return fmt.Errorf("failed to parse extraConfig: %s", v) - } else if r[1] == "" { - return fmt.Errorf("empty value: %s", v) } *e = append(*e, &types.OptionValue{Key: r[0], Value: r[1]}) return nil @@ -73,8 +71,13 @@ func (cmd *change) Register(ctx context.Context, f *flag.FlagSet) { func (cmd *change) Description() string { return `Change VM configuration. +To add ExtraConfig variables that can read within the guest, use the 'guestinfo.' prefix. + Examples: - govc vm.change -vm $vm -e smc.present=TRUE -e ich7m.present=TRUE` + govc vm.change -vm $vm -e smc.present=TRUE -e ich7m.present=TRUE + govc vm.change -vm $vm -e guestinfo.vmname $vm + # Read the variable set above inside the guest: + vmware-rpctool "info-get guestinfo.vmname"` } func (cmd *change) Process(ctx context.Context) error { diff --git a/vendor/github.com/vmware/govmomi/govc/vm/disk/create.go b/vendor/github.com/vmware/govmomi/govc/vm/disk/create.go index d297a3cd..4c0aac4e 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/disk/create.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/disk/create.go @@ -89,6 +89,13 @@ func (cmd *create) Process(ctx context.Context) error { return nil } +func (cmd *create) Description() string { + return `Create disk and attach to VM. + +Examples: + govc vm.disk.create -vm $name -name $name/disk1 -size 10G` +} + func (cmd *create) Run(ctx context.Context, f *flag.FlagSet) error { if len(cmd.Name) == 0 { return errors.New("please specify a disk name") diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/chmod.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/chmod.go index ed634c23..29f0745b 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/guest/chmod.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/chmod.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -19,13 +19,14 @@ package guest import ( "context" "flag" + "strconv" "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/vim25/types" ) type chmod struct { *GuestFlag - *FileAttrFlag } func init() { @@ -35,25 +36,42 @@ func init() { func (cmd *chmod) Register(ctx context.Context, f *flag.FlagSet) { cmd.GuestFlag, ctx = newGuestFlag(ctx) cmd.GuestFlag.Register(ctx, f) - cmd.FileAttrFlag, ctx = newFileAttrFlag(ctx) - cmd.FileAttrFlag.Register(ctx, f) } func (cmd *chmod) Process(ctx context.Context) error { if err := cmd.GuestFlag.Process(ctx); err != nil { return err } - if err := cmd.FileAttrFlag.Process(ctx); err != nil { - return err - } return nil } +func (cmd *chmod) Usage() string { + return "MODE FILE" +} + +func (cmd *chmod) Description() string { + return `Change FILE MODE on VM. + +Examples: + govc guest.chmod -vm $name 0644 /var/log/foo.log` +} + func (cmd *chmod) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() != 2 { + return flag.ErrHelp + } + m, err := cmd.FileManager() if err != nil { return err } - return m.ChangeFileAttributes(ctx, cmd.Auth(), f.Arg(0), cmd.Attr()) + var attr types.GuestPosixFileAttributes + + attr.Permissions, err = strconv.ParseInt(f.Arg(0), 0, 64) + if err != nil { + return err + } + + return m.ChangeFileAttributes(ctx, cmd.Auth(), f.Arg(1), &attr) } diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/chown.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/chown.go new file mode 100644 index 00000000..0b6a455a --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/chown.go @@ -0,0 +1,96 @@ +/* +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 guest + +import ( + "context" + "flag" + "strconv" + "strings" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/vim25/types" +) + +type chown struct { + *GuestFlag +} + +func init() { + cli.Register("guest.chown", &chown{}) +} + +func (cmd *chown) Register(ctx context.Context, f *flag.FlagSet) { + cmd.GuestFlag, ctx = newGuestFlag(ctx) + cmd.GuestFlag.Register(ctx, f) +} + +func (cmd *chown) Process(ctx context.Context) error { + if err := cmd.GuestFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *chown) Usage() string { + return "UID[:GID] FILE" +} + +func (cmd *chown) Description() string { + return `Change FILE UID and GID on VM. + +Examples: + govc guest.chown -vm $name UID[:GID] /var/log/foo.log` +} + +func (cmd *chown) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() != 2 { + return flag.ErrHelp + } + + m, err := cmd.FileManager() + if err != nil { + return err + } + + var attr types.GuestPosixFileAttributes + + ids := strings.SplitN(f.Arg(0), ":", 2) + if len(ids) == 0 { + return flag.ErrHelp + } + + id, err := strconv.Atoi(ids[0]) + if err != nil { + return err + } + + attr.OwnerId = new(int32) + *attr.OwnerId = int32(id) + + if len(ids) == 2 { + id, err = strconv.Atoi(ids[1]) + if err != nil { + return err + } + + attr.GroupId = new(int32) + *attr.GroupId = int32(id) + } + + return m.ChangeFileAttributes(ctx, cmd.Auth(), f.Arg(1), &attr) +} diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/download.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/download.go index 9e336d0b..4f0d789e 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/guest/download.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/download.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -24,7 +24,7 @@ import ( "os" "github.com/vmware/govmomi/govc/cli" - "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/progress" ) type download struct { @@ -70,50 +70,36 @@ func (cmd *download) Run(ctx context.Context, f *flag.FlagSet) error { return flag.ErrHelp } - m, err := cmd.FileManager() - if err != nil { - return err - } - src := f.Arg(0) dst := f.Arg(1) - _, err = os.Stat(dst) + _, err := os.Stat(dst) if err == nil && !cmd.overwrite { return os.ErrExist } - info, err := m.InitiateFileTransferFromGuest(ctx, cmd.Auth(), src) + c, err := cmd.Toolbox() if err != nil { return err } - u, err := cmd.ParseURL(info.Url) + s, n, err := c.Download(ctx, src) if err != nil { return err } - c, err := cmd.Client() - if err != nil { - return nil - } - - p := soap.DefaultDownload - if dst == "-" { - f, _, err := c.Client.Download(u, &p) - if err != nil { - return err - } - _, err = io.Copy(os.Stdout, f) + _, err = io.Copy(os.Stdout, s) return err } + var p progress.Sinker + if cmd.OutputFlag.TTY { logger := cmd.ProgressLogger("Downloading... ") - p.Progress = logger + p = logger defer logger.Wait() } - return c.Client.DownloadFile(dst, u, &p) + return c.ProcessManager.Client().WriteFile(dst, s, n, p) } diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/file_attr.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/file_attr.go index 3ff3b6bb..3e18602f 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/guest/file_attr.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/file_attr.go @@ -33,8 +33,8 @@ func newFileAttrFlag(ctx context.Context) (*FileAttrFlag, context.Context) { } func (flag *FileAttrFlag) Register(ctx context.Context, f *flag.FlagSet) { - f.Var(flags.NewInt32(&flag.OwnerId), "uid", "User ID") - f.Var(flags.NewInt32(&flag.GroupId), "gid", "Group ID") + f.Var(flags.NewOptionalInt32(&flag.OwnerId), "uid", "User ID") + f.Var(flags.NewOptionalInt32(&flag.GroupId), "gid", "Group ID") f.Int64Var(&flag.Permissions, "perm", 0, "File permissions") } diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/getenv.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/getenv.go index 7d850aca..dc0c2b42 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/guest/getenv.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/getenv.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -44,6 +44,18 @@ func (cmd *getenv) Process(ctx context.Context) error { return nil } +func (cmd *getenv) Usage() string { + return "[NAME]..." +} + +func (cmd *getenv) Description() string { + return `Read NAME environment variables from VM. + +Examples: + govc guest.getenv -vm $name + govc guest.getenv -vm $name HOME` +} + func (cmd *getenv) Run(ctx context.Context, f *flag.FlagSet) error { m, err := cmd.ProcessManager() if err != nil { diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/guest.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/guest.go index 4018bf0b..b71dfdda 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/guest/guest.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/guest.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -25,6 +25,7 @@ import ( "github.com/vmware/govmomi/govc/flags" "github.com/vmware/govmomi/guest" + "github.com/vmware/govmomi/guest/toolbox" "github.com/vmware/govmomi/object" ) @@ -62,6 +63,24 @@ func (flag *GuestFlag) Process(ctx context.Context) error { return nil } +func (flag *GuestFlag) Toolbox() (*toolbox.Client, error) { + pm, err := flag.ProcessManager() + if err != nil { + return nil, err + } + + fm, err := flag.FileManager() + if err != nil { + return nil, err + } + + return &toolbox.Client{ + ProcessManager: pm, + FileManager: fm, + Authentication: flag.Auth(), + }, nil +} + func (flag *GuestFlag) FileManager() (*guest.FileManager, error) { ctx := context.TODO() c, err := flag.Client() diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/kill.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/kill.go index 0f008f21..ef1273d8 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/guest/kill.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/kill.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -47,6 +47,13 @@ func (cmd *kill) Process(ctx context.Context) error { return nil } +func (cmd *kill) Description() string { + return `Kill process ID on VM. + +Examples: + govc guest.kill -vm $name -p 12345` +} + func (cmd *kill) Run(ctx context.Context, f *flag.FlagSet) error { m, err := cmd.ProcessManager() if err != nil { diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/ls.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/ls.go index dc95ab02..2d236ae0 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/guest/ls.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/ls.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -24,10 +24,14 @@ import ( "text/tabwriter" "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/units" + "github.com/vmware/govmomi/vim25/types" ) type ls struct { *GuestFlag + + simple bool } func init() { @@ -37,6 +41,8 @@ func init() { func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) { cmd.GuestFlag, ctx = newGuestFlag(ctx) cmd.GuestFlag.Register(ctx, f) + + f.BoolVar(&cmd.simple, "s", false, "Simple path only listing") // sadly we used '-l' for guest login } func (cmd *ls) Process(ctx context.Context) error { @@ -46,6 +52,17 @@ func (cmd *ls) Process(ctx context.Context) error { return nil } +func (cmd *ls) Usage() string { + return "PATH" +} + +func (cmd *ls) Description() string { + return `List PATH files in VM. + +Examples: + govc guest.ls -vm $name /tmp` +} + func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { m, err := cmd.FileManager() if err != nil { @@ -53,7 +70,7 @@ func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { } var offset int32 - tw := tabwriter.NewWriter(os.Stdout, 3, 0, 2, ' ', 0) + tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0) for { info, err := m.ListFiles(ctx, cmd.Auth(), f.Arg(0), offset, 0, f.Arg(1)) @@ -62,8 +79,38 @@ func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { } for _, f := range info.Files { - attr := f.Attributes.GetGuestFileAttributes() // TODO: GuestPosixFileAttributes - fmt.Fprintf(tw, "%d\t%s\t%s\n", f.Size, attr.ModificationTime.Format("Mon Jan 2 15:04:05 2006"), f.Path) + if cmd.simple { + fmt.Fprintln(tw, f.Path) + continue + } + + var kind byte + + switch types.GuestFileType(f.Type) { + case types.GuestFileTypeDirectory: + kind = 'd' + if f.Size == 0 { + f.Size = 4092 + } + case types.GuestFileTypeSymlink: + kind = 'l' + case types.GuestFileTypeFile: + kind = '-' + } + + switch x := f.Attributes.(type) { + case *types.GuestPosixFileAttributes: + perm := os.FileMode(x.Permissions).Perm().String()[1:] + fmt.Fprintf(tw, "%c%s\t%d\t%d\t", kind, perm, *x.OwnerId, *x.GroupId) + } + + attr := f.Attributes.GetGuestFileAttributes() + + fmt.Fprintf(tw, "%s\t%s\t%s", units.FileSize(f.Size), attr.ModificationTime.Format("Jan 2 15:04 2006"), f.Path) + if attr.SymlinkTarget != "" { + fmt.Fprintf(tw, " -> %s", attr.SymlinkTarget) + } + fmt.Fprintln(tw) } _ = tw.Flush() diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/mkdir.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/mkdir.go index 7b317108..541f6d7f 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/guest/mkdir.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/mkdir.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -49,6 +49,18 @@ func (cmd *mkdir) Process(ctx context.Context) error { return nil } +func (cmd *mkdir) Usage() string { + return "PATH" +} + +func (cmd *mkdir) Description() string { + return `Create directory PATH in VM. + +Examples: + govc guest.mkdir -vm $name /tmp/logs + govc guest.mkdir -vm $name -p /tmp/logs/foo/bar` +} + func (cmd *mkdir) Run(ctx context.Context, f *flag.FlagSet) error { m, err := cmd.FileManager() if err != nil { diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/mktemp.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/mktemp.go index 2769e883..3637bcef 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/guest/mktemp.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/mktemp.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -28,6 +28,7 @@ type mktemp struct { *GuestFlag dir bool + path string prefix string suffix string } @@ -41,6 +42,7 @@ func (cmd *mktemp) Register(ctx context.Context, f *flag.FlagSet) { cmd.GuestFlag.Register(ctx, f) f.BoolVar(&cmd.dir, "d", false, "Make a directory instead of a file") + f.StringVar(&cmd.path, "p", "", "If specified, create relative to this directory") f.StringVar(&cmd.prefix, "t", "", "Prefix") f.StringVar(&cmd.suffix, "s", "", "Suffix") } @@ -52,6 +54,16 @@ func (cmd *mktemp) Process(ctx context.Context) error { return nil } +func (cmd *mktemp) Description() string { + return `Create a temporary file or directory in VM. + +Examples: + govc guest.mktemp -vm $name + govc guest.mktemp -vm $name -d + govc guest.mktemp -vm $name -t myprefix + govc guest.mktemp -vm $name -p /var/tmp/$USER` +} + func (cmd *mktemp) Run(ctx context.Context, f *flag.FlagSet) error { m, err := cmd.FileManager() if err != nil { @@ -63,7 +75,7 @@ func (cmd *mktemp) Run(ctx context.Context, f *flag.FlagSet) error { mk = m.CreateTemporaryDirectory } - name, err := mk(ctx, cmd.Auth(), cmd.prefix, cmd.suffix) + name, err := mk(ctx, cmd.Auth(), cmd.prefix, cmd.suffix, cmd.path) if err != nil { return err } diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/mv.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/mv.go new file mode 100644 index 00000000..4890a6f8 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/mv.go @@ -0,0 +1,85 @@ +/* +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 guest + +import ( + "context" + "flag" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type mv struct { + *GuestFlag + + noclobber bool +} + +func init() { + cli.Register("guest.mv", &mv{}) +} + +func (cmd *mv) Register(ctx context.Context, f *flag.FlagSet) { + cmd.GuestFlag, ctx = newGuestFlag(ctx) + cmd.GuestFlag.Register(ctx, f) + + f.BoolVar(&cmd.noclobber, "n", false, "Do not overwrite an existing file") +} + +func (cmd *mv) Process(ctx context.Context) error { + if err := cmd.GuestFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *mv) Usage() string { + return "SOURCE DEST" +} + +func (cmd *mv) Description() string { + return `Move (rename) files in VM. + +Examples: + govc guest.mv -vm $name /tmp/foo.sh /tmp/bar.sh + govc guest.mv -vm $name -n /tmp/baz.sh /tmp/bar.sh` +} + +func (cmd *mv) Run(ctx context.Context, f *flag.FlagSet) error { + m, err := cmd.FileManager() + if err != nil { + return err + } + + src := f.Arg(0) + dst := f.Arg(1) + + err = m.MoveFile(ctx, cmd.Auth(), src, dst, !cmd.noclobber) + + if err != nil { + if soap.IsSoapFault(err) { + soapFault := soap.ToSoapFault(err) + if _, ok := soapFault.VimFault().(types.NotAFile); ok { + err = m.MoveDirectory(ctx, cmd.Auth(), src, dst) + } + } + } + + return err +} diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/ps.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/ps.go index f7a99154..217986b9 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/guest/ps.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/ps.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -21,8 +21,8 @@ import ( "flag" "fmt" "io" - "os" "strconv" + "strings" "text/tabwriter" "time" @@ -36,6 +36,7 @@ type ps struct { *GuestFlag every bool + exit bool wait bool pids pidSelector @@ -81,6 +82,7 @@ func (cmd *ps) Register(ctx context.Context, f *flag.FlagSet) { cmd.uids = make(map[string]bool) f.BoolVar(&cmd.every, "e", false, "Select all processes") + f.BoolVar(&cmd.exit, "x", false, "Output exit time and code") f.BoolVar(&cmd.wait, "X", false, "Wait for process to exit") f.Var(&cmd.pids, "p", "Select by process ID") f.Var(&cmd.uids, "U", "Select by process UID") @@ -96,6 +98,22 @@ func (cmd *ps) Process(ctx context.Context) error { return nil } +func (cmd *ps) Description() string { + return `List processes in VM. + +By default, unless the '-e', '-p' or '-U' flag is specified, only processes owned +by the '-l' flag user are displayed. + +The '-x' and '-X' flags only apply to processes started by vmware-tools, +such as those started with the govc guest.start command. + +Examples: + govc guest.ps -vm $name + govc guest.ps -vm $name -e + govc guest.ps -vm $name -p 12345 + govc guest.ps -vm $name -U root` +} + func running(procs []types.GuestProcessInfo) bool { for _, p := range procs { if p.EndTime == nil { @@ -145,9 +163,17 @@ type psResult struct { } func (r *psResult) Write(w io.Writer) error { - tw := tabwriter.NewWriter(os.Stdout, 4, 0, 2, ' ', 0) + tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0) + + fmt.Fprintf(tw, "%s\t%s\t%s", "UID", "PID", "STIME") + if r.cmd.exit { + fmt.Fprintf(tw, "\t%s\t%s", "XTIME", "XCODE") + } + fmt.Fprint(tw, "\tCMD\n") - fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", "UID", "PID", "STIME", "CMD") + if len(r.cmd.pids) != 0 { + r.cmd.every = true + } if !r.cmd.every && len(r.cmd.uids) == 0 { r.cmd.uids[r.cmd.auth.Username] = true @@ -155,7 +181,17 @@ func (r *psResult) Write(w io.Writer) error { for _, p := range r.ProcessInfo { if r.cmd.every || r.cmd.uids[p.Owner] { - fmt.Fprintf(tw, "%s\t%d\t%s\t%s\n", p.Owner, p.Pid, p.StartTime.Format("15:04"), p.CmdLine) + fmt.Fprintf(tw, "%s\t%d\t%s", p.Owner, p.Pid, p.StartTime.Format("15:04")) + if r.cmd.exit { + etime := "-" + ecode := "-" + if p.EndTime != nil { + etime = p.EndTime.Format("15:04") + ecode = strconv.Itoa(int(p.ExitCode)) + } + fmt.Fprintf(tw, "\t%s\t%s", etime, ecode) + } + fmt.Fprintf(tw, "\t%s\n", strings.TrimSpace(p.CmdLine)) } } diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/rm.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/rm.go index 9592f146..bd21a55e 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/guest/rm.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/rm.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -43,6 +43,17 @@ func (cmd *rm) Process(ctx context.Context) error { return nil } +func (cmd *rm) Usage() string { + return "PATH" +} + +func (cmd *rm) Description() string { + return `Remove file PATH in VM. + +Examples: + govc guest.rm -vm $name /tmp/foo.log` +} + func (cmd *rm) Run(ctx context.Context, f *flag.FlagSet) error { m, err := cmd.FileManager() if err != nil { diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/rmdir.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/rmdir.go index 0843a4df..7b69a879 100644 --- a/vendor/github.com/vmware/govmomi/govc/vm/guest/rmdir.go +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/rmdir.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -47,6 +47,18 @@ func (cmd *rmdir) Process(ctx context.Context) error { return nil } +func (cmd *rmdir) Usage() string { + return "PATH" +} + +func (cmd *rmdir) Description() string { + return `Remove directory PATH in VM. + +Examples: + govc guest.rmdir -vm $name /tmp/empty-dir + govc guest.rmdir -vm $name -r /tmp/non-empty-dir` +} + func (cmd *rmdir) Run(ctx context.Context, f *flag.FlagSet) error { m, err := cmd.FileManager() if err != nil { diff --git a/vendor/github.com/vmware/govmomi/govc/vm/guest/run.go b/vendor/github.com/vmware/govmomi/govc/vm/guest/run.go new file mode 100644 index 00000000..84dca194 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/govc/vm/guest/run.go @@ -0,0 +1,156 @@ +/* +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 guest + +import ( + "bytes" + "context" + "flag" + "fmt" + "io" + "net/http" + "os" + "os/exec" + "strings" + + "github.com/vmware/govmomi/govc/cli" +) + +type run struct { + *GuestFlag + + data string + verbose bool + dir string + vars env +} + +func init() { + cli.Register("guest.run", &run{}) +} + +func (cmd *run) Register(ctx context.Context, f *flag.FlagSet) { + cmd.GuestFlag, ctx = newGuestFlag(ctx) + cmd.GuestFlag.Register(ctx, f) + + f.StringVar(&cmd.data, "d", "", "Input data") + f.BoolVar(&cmd.verbose, "v", false, "Verbose") + f.StringVar(&cmd.dir, "C", "", "The absolute path of the working directory for the program to start") + f.Var(&cmd.vars, "e", "Set environment variable or HTTP header") +} + +func (cmd *run) Process(ctx context.Context) error { + if err := cmd.GuestFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *run) Usage() string { + return "NAME [ARG]..." +} + +func (cmd *run) Description() string { + return `Run program NAME in VM and display output. + +This command depends on govmomi/toolbox running in the VM guest and does not work with standard VMware tools. + +If the program NAME is an HTTP verb, the toolbox's http.RoundTripper will be used as the HTTP transport. + +Examples: + govc guest.run -vm $name kubectl get pods + govc guest.run -vm $name -d - kubectl create -f - 0 && nc.NicType == kind { + ip := net.ParseIP(nc.CandidateVnic[0].Spec.Ip.IpAddress) + if ip != nil { + mname = ip.String() + m.mu.Lock() + m.hosts[name] = mname + m.mu.Unlock() + name = mname + break + } + } + } + + url.Host = net.JoinHostPort(name, port) + + m.c.SetThumbprint(url.Host, host.Summary.Config.SslThumbprint) + + return url, nil +} + func (m FileManager) InitiateFileTransferFromGuest(ctx context.Context, auth types.BaseGuestAuthentication, guestFilePath string) (*types.FileTransferInformation, error) { req := types.InitiateFileTransferFromGuest{ This: m.Reference(), diff --git a/vendor/github.com/vmware/govmomi/guest/operations_manager.go b/vendor/github.com/vmware/govmomi/guest/operations_manager.go index 016503c8..d02fb4ea 100644 --- a/vendor/github.com/vmware/govmomi/guest/operations_manager.go +++ b/vendor/github.com/vmware/govmomi/guest/operations_manager.go @@ -18,6 +18,7 @@ package guest import ( "context" + "sync" "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/vim25" @@ -58,7 +59,13 @@ func (m OperationsManager) FileManager(ctx context.Context) (*FileManager, error return nil, err } - return &FileManager{*g.FileManager, m.vm, m.c}, nil + return &FileManager{ + ManagedObjectReference: *g.FileManager, + vm: m.vm, + c: m.c, + mu: new(sync.Mutex), + hosts: make(map[string]string), + }, nil } func (m OperationsManager) ProcessManager(ctx context.Context) (*ProcessManager, error) { diff --git a/vendor/github.com/vmware/govmomi/guest/process_manager.go b/vendor/github.com/vmware/govmomi/guest/process_manager.go index 55db1e39..74741a1c 100644 --- a/vendor/github.com/vmware/govmomi/guest/process_manager.go +++ b/vendor/github.com/vmware/govmomi/guest/process_manager.go @@ -32,6 +32,10 @@ type ProcessManager struct { c *vim25.Client } +func (m ProcessManager) Client() *vim25.Client { + return m.c +} + func (m ProcessManager) Reference() types.ManagedObjectReference { return m.ManagedObjectReference } diff --git a/vendor/github.com/vmware/govmomi/guest/toolbox/client.go b/vendor/github.com/vmware/govmomi/guest/toolbox/client.go new file mode 100644 index 00000000..5244e310 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/guest/toolbox/client.go @@ -0,0 +1,345 @@ +/* +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 toolbox + +import ( + "bufio" + "bytes" + "context" + "fmt" + "io" + "net/http" + "net/url" + "os" + "os/exec" + "strings" + + "github.com/vmware/govmomi/guest" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +// Client attempts to expose guest.OperationsManager as idiomatic Go interfaces +type Client struct { + ProcessManager *guest.ProcessManager + FileManager *guest.FileManager + Authentication types.BaseGuestAuthentication +} + +// procReader retries InitiateFileTransferFromGuest calls if toolbox is still running the process. +// See also: ProcessManager.Stat +func (c *Client) procReader(ctx context.Context, src string) (*types.FileTransferInformation, error) { + for { + info, err := c.FileManager.InitiateFileTransferFromGuest(ctx, c.Authentication, src) + if err != nil { + if soap.IsSoapFault(err) { + if _, ok := soap.ToSoapFault(err).VimFault().(types.CannotAccessFile); ok { + // We're not waiting in between retries since ProcessManager.Stat + // has already waited. In the case that this client was pointed at + // standard vmware-tools, the types.NotFound fault would have been + // returned since the file "/proc/$pid/stdout" does not exist - in + // which case, we won't retry at all. + continue + } + } + + return nil, err + } + + return info, err + } +} + +// RoundTrip implements http.RoundTripper over vmx guest RPC. +// This transport depends on govmomi/toolbox running in the VM guest and does not work with standard VMware tools. +// Using this transport makes it is possible to connect to HTTP endpoints that are bound to the VM's loopback address. +// Note that the toolbox's http.RoundTripper only supports the "http" scheme, "https" is not supported. +func (c *Client) RoundTrip(req *http.Request) (*http.Response, error) { + if req.URL.Scheme != "http" { + return nil, fmt.Errorf("%q scheme not supported", req.URL.Scheme) + } + + ctx := req.Context() + + req.Header.Set("Connection", "close") // we need the server to close the connection after 1 request + + spec := types.GuestProgramSpec{ + ProgramPath: "http.RoundTrip", + Arguments: req.URL.Host, + } + + pid, err := c.ProcessManager.StartProgram(ctx, c.Authentication, &spec) + if err != nil { + return nil, err + } + + dst := fmt.Sprintf("/proc/%d/stdin", pid) + src := fmt.Sprintf("/proc/%d/stdout", pid) + + var buf bytes.Buffer + err = req.Write(&buf) + if err != nil { + return nil, err + } + + attr := new(types.GuestPosixFileAttributes) + size := int64(buf.Len()) + + url, err := c.FileManager.InitiateFileTransferToGuest(ctx, c.Authentication, dst, attr, size, true) + if err != nil { + return nil, err + } + + vc := c.ProcessManager.Client() + + u, err := c.FileManager.TransferURL(ctx, url) + if err != nil { + return nil, err + } + + p := soap.DefaultUpload + p.ContentLength = size + + err = vc.Client.Upload(&buf, u, &p) + if err != nil { + return nil, err + } + + info, err := c.procReader(ctx, src) + if err != nil { + return nil, err + } + + u, err = c.FileManager.TransferURL(ctx, info.Url) + if err != nil { + return nil, err + } + + f, _, err := vc.Client.Download(u, &soap.DefaultDownload) + if err != nil { + return nil, err + } + + return http.ReadResponse(bufio.NewReader(f), req) +} + +// Run implements exec.Cmd.Run over vmx guest RPC. +func (c *Client) Run(ctx context.Context, cmd *exec.Cmd) error { + vc := c.ProcessManager.Client() + + spec := types.GuestProgramSpec{ + ProgramPath: cmd.Path, + Arguments: strings.Join(cmd.Args, " "), + EnvVariables: cmd.Env, + WorkingDirectory: cmd.Dir, + } + + pid, serr := c.ProcessManager.StartProgram(ctx, c.Authentication, &spec) + if serr != nil { + return serr + } + + if cmd.Stdin != nil { + dst := fmt.Sprintf("/proc/%d/stdin", pid) + + var buf bytes.Buffer + size, err := io.Copy(&buf, cmd.Stdin) + if err != nil { + return err + } + + attr := new(types.GuestPosixFileAttributes) + + url, err := c.FileManager.InitiateFileTransferToGuest(ctx, c.Authentication, dst, attr, size, true) + if err != nil { + return err + } + + u, err := c.FileManager.TransferURL(ctx, url) + if err != nil { + return err + } + + p := soap.DefaultUpload + p.ContentLength = size + + err = vc.Client.Upload(&buf, u, &p) + if err != nil { + return err + } + } + + names := []string{"out", "err"} + + for i, w := range []io.Writer{cmd.Stdout, cmd.Stderr} { + if w == nil { + continue + } + + src := fmt.Sprintf("/proc/%d/std%s", pid, names[i]) + + info, err := c.procReader(ctx, src) + if err != nil { + return err + } + + u, err := c.FileManager.TransferURL(ctx, info.Url) + if err != nil { + return err + } + + f, _, err := vc.Client.Download(u, &soap.DefaultDownload) + if err != nil { + return err + } + + _, err = io.Copy(w, f) + _ = f.Close() + if err != nil { + return err + } + } + + procs, err := c.ProcessManager.ListProcesses(ctx, c.Authentication, []int64{pid}) + if err != nil { + return err + } + + if len(procs) == 1 { + rc := procs[0].ExitCode + if rc != 0 { + return fmt.Errorf("%s: exit %d", cmd.Path, rc) + } + } + + return nil +} + +// archiveReader wraps an io.ReadCloser to support streaming download +// of a guest directory, stops reading once it sees the stream trailer. +// This is only useful when guest tools is the Go toolbox. +// The trailer is required since TransferFromGuest requires a Content-Length, +// which toolbox doesn't know ahead of time as the gzip'd tarball never touches the disk. +// We opted to wrap this here for now rather than guest.FileManager so +// DownloadFile can be also be used as-is to handle this use case. +type archiveReader struct { + io.ReadCloser +} + +var ( + gzipHeader = []byte{0x1f, 0x8b, 0x08} // rfc1952 {ID1, ID2, CM} + gzipHeaderLen = len(gzipHeader) +) + +func (r *archiveReader) Read(buf []byte) (int, error) { + nr, err := r.ReadCloser.Read(buf) + + // Stop reading if the last N bytes are the gzipTrailer + if nr >= gzipHeaderLen { + if bytes.Equal(buf[nr-gzipHeaderLen:nr], gzipHeader) { + nr -= gzipHeaderLen + err = io.EOF + } + } + + return nr, err +} + +func isDir(src string) bool { + u, err := url.Parse(src) + if err != nil { + return false + } + + return strings.HasSuffix(u.Path, "/") +} + +// Download initiates a file transfer from the guest +func (c *Client) Download(ctx context.Context, src string) (io.ReadCloser, int64, error) { + vc := c.ProcessManager.Client() + + info, err := c.FileManager.InitiateFileTransferFromGuest(ctx, c.Authentication, src) + if err != nil { + return nil, 0, err + } + + u, err := c.FileManager.TransferURL(ctx, info.Url) + if err != nil { + return nil, 0, err + } + + p := soap.DefaultDownload + + f, n, err := vc.Download(u, &p) + if err != nil { + return nil, n, err + } + + if strings.HasPrefix(src, "/archive:/") || isDir(src) { + f = &archiveReader{ReadCloser: f} // look for the gzip trailer + } + + return f, n, nil +} + +// Upload transfers a file to the guest +func (c *Client) Upload(ctx context.Context, src io.Reader, dst string, p soap.Upload, attr types.BaseGuestFileAttributes, force bool) error { + vc := c.ProcessManager.Client() + + var err error + + if p.ContentLength == 0 { // Content-Length is required + switch r := src.(type) { + case *bytes.Buffer: + p.ContentLength = int64(r.Len()) + case *bytes.Reader: + p.ContentLength = int64(r.Len()) + case *strings.Reader: + p.ContentLength = int64(r.Len()) + case *os.File: + info, serr := r.Stat() + if serr != nil { + return serr + } + + p.ContentLength = info.Size() + } + + if p.ContentLength == 0 { // os.File for example could be a device (stdin) + buf := new(bytes.Buffer) + + p.ContentLength, err = io.Copy(buf, src) + if err != nil { + return err + } + + src = buf + } + } + + url, err := c.FileManager.InitiateFileTransferToGuest(ctx, c.Authentication, dst, attr, p.ContentLength, force) + if err != nil { + return err + } + + u, err := c.FileManager.TransferURL(ctx, url) + if err != nil { + return err + } + + return vc.Client.Upload(src, u, &p) +} diff --git a/vendor/github.com/vmware/govmomi/list/lister.go b/vendor/github.com/vmware/govmomi/list/lister.go index ae162b7f..2ee32e6b 100644 --- a/vendor/github.com/vmware/govmomi/list/lister.go +++ b/vendor/github.com/vmware/govmomi/list/lister.go @@ -33,6 +33,10 @@ type Element struct { 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 @@ -112,23 +116,6 @@ type Lister struct { All bool } -func traversable(ref types.ManagedObjectReference) bool { - switch ref.Type { - case "Folder": - case "Datacenter": - case "ComputeResource", "ClusterComputeResource": - // Treat ComputeResource and ClusterComputeResource as one and the same. - // It doesn't matter from the perspective of the lister. - case "HostSystem": - case "VirtualApp": - case "StoragePod": - default: - return false - } - - return true -} - func (l Lister) retrieveProperties(ctx context.Context, req types.RetrieveProperties, dst *[]interface{}) error { res, err := l.Collector.RetrieveProperties(ctx, req) if err != nil { @@ -225,6 +212,8 @@ func (l Lister) ListFolder(ctx context.Context) ([]Element, error) { // 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 @@ -286,7 +275,7 @@ func (l Lister) ListDatacenter(ctx context.Context) ([]Element, error) { if l.All { pspec.All = types.NewBool(true) } else { - pspec.PathSet = []string{"name"} + pspec.PathSet = []string{"name", "childType"} } req := types.RetrieveProperties{ diff --git a/vendor/github.com/vmware/govmomi/list/recurser.go b/vendor/github.com/vmware/govmomi/list/recurser.go deleted file mode 100644 index 32a67829..00000000 --- a/vendor/github.com/vmware/govmomi/list/recurser.go +++ /dev/null @@ -1,97 +0,0 @@ -/* -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 list - -import ( - "context" - "path" - "path/filepath" - - "github.com/vmware/govmomi/property" -) - -type Recurser struct { - Collector *property.Collector - - // All configures the recurses to fetch complete objects for leaf nodes. - All bool - - // TraverseLeafs configures the Recurser to traverse 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. - TraverseLeafs bool -} - -func (r Recurser) Recurse(ctx context.Context, root Element, parts []string) ([]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 TraverseLeafs - // field is set to false. - // - if !traversable(root.Object.Reference()) || !r.TraverseLeafs { - return []Element{root}, nil - } - } - - k := 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 - } - - pattern := parts[0] - parts = parts[1:] - - var out []Element - for _, e := range in { - matched, err := filepath.Match(pattern, path.Base(e.Path)) - if err != nil { - return nil, err - } - - if !matched { - continue - } - - nres, err := r.Recurse(ctx, e, parts) - if err != nil { - return nil, err - } - - out = append(out, nres...) - } - - return out, nil -} diff --git a/vendor/github.com/vmware/govmomi/nfc/lease.go b/vendor/github.com/vmware/govmomi/nfc/lease.go new file mode 100644 index 00000000..20323805 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/nfc/lease.go @@ -0,0 +1,205 @@ +/* +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 nfc + +import ( + "context" + "errors" + "fmt" + "io" + + "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/progress" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type Lease struct { + types.ManagedObjectReference + + c *vim25.Client +} + +func NewLease(c *vim25.Client, ref types.ManagedObjectReference) *Lease { + return &Lease{ref, c} +} + +// Abort wraps methods.Abort +func (l *Lease) Abort(ctx context.Context, fault *types.LocalizedMethodFault) error { + req := types.HttpNfcLeaseAbort{ + This: l.Reference(), + Fault: fault, + } + + _, err := methods.HttpNfcLeaseAbort(ctx, l.c, &req) + if err != nil { + return err + } + + return nil +} + +// Complete wraps methods.Complete +func (l *Lease) Complete(ctx context.Context) error { + req := types.HttpNfcLeaseComplete{ + This: l.Reference(), + } + + _, err := methods.HttpNfcLeaseComplete(ctx, l.c, &req) + if err != nil { + return err + } + + return nil +} + +// GetManifest wraps methods.GetManifest +func (l *Lease) GetManifest(ctx context.Context) error { + req := types.HttpNfcLeaseGetManifest{ + This: l.Reference(), + } + + _, err := methods.HttpNfcLeaseGetManifest(ctx, l.c, &req) + if err != nil { + return err + } + + return nil +} + +// Progress wraps methods.Progress +func (l *Lease) Progress(ctx context.Context, percent int32) error { + req := types.HttpNfcLeaseProgress{ + This: l.Reference(), + Percent: percent, + } + + _, err := methods.HttpNfcLeaseProgress(ctx, l.c, &req) + if err != nil { + return err + } + + return nil +} + +type LeaseInfo struct { + types.HttpNfcLeaseInfo + + Items []FileItem +} + +func (l *Lease) newLeaseInfo(li *types.HttpNfcLeaseInfo, items []types.OvfFileItem) (*LeaseInfo, error) { + info := &LeaseInfo{ + HttpNfcLeaseInfo: *li, + } + + for _, device := range li.DeviceUrl { + u, err := l.c.ParseURL(device.Url) + if err != nil { + return nil, err + } + + if device.SslThumbprint != "" { + // TODO: prefer host management IP + l.c.SetThumbprint(u.Host, device.SslThumbprint) + } + + for _, item := range items { + if device.ImportKey == item.DeviceId { + info.Items = append(info.Items, NewFileItem(u, item)) + break + } + } + } + + return info, nil +} + +func (l *Lease) Wait(ctx context.Context, items []types.OvfFileItem) (*LeaseInfo, error) { + var lease mo.HttpNfcLease + + pc := property.DefaultCollector(l.c) + err := property.Wait(ctx, pc, l.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 l.newLeaseInfo(lease.Info, items) + } + + if lease.Error != nil { + return nil, errors.New(lease.Error.LocalizedMessage) + } + + return nil, fmt.Errorf("unexpected nfc lease state: %s", lease.State) +} + +func (l *Lease) StartUpdater(ctx context.Context, info *LeaseInfo) *LeaseUpdater { + return newLeaseUpdater(ctx, l, info) +} + +func (l *Lease) Upload(ctx context.Context, item FileItem, f io.Reader, opts soap.Upload) error { + if opts.Progress == nil { + opts.Progress = item + } else { + opts.Progress = progress.Tee(item, opts.Progress) + } + + // Non-disk files (such as .iso) use the PUT method. + // Overwrite: t header is also required in this case (ovftool does the same) + if item.Create { + opts.Method = "PUT" + opts.Headers = map[string]string{ + "Overwrite": "t", + } + } else { + opts.Method = "POST" + opts.Type = "application/x-vnd.vmware-streamVmdk" + } + + return l.c.Upload(f, item.URL, &opts) +} diff --git a/vendor/github.com/vmware/govmomi/govc/importx/lease_updater.go b/vendor/github.com/vmware/govmomi/nfc/lease_updater.go similarity index 72% rename from vendor/github.com/vmware/govmomi/govc/importx/lease_updater.go rename to vendor/github.com/vmware/govmomi/nfc/lease_updater.go index aa05bd49..0b9757d3 100644 --- a/vendor/github.com/vmware/govmomi/govc/importx/lease_updater.go +++ b/vendor/github.com/vmware/govmomi/nfc/lease_updater.go @@ -14,35 +14,41 @@ See the License for the specific language governing permissions and limitations under the License. */ -package importx +package nfc import ( "context" - "fmt" + "log" "net/url" "sync" "sync/atomic" "time" - "github.com/vmware/govmomi/object" - "github.com/vmware/govmomi/vim25" "github.com/vmware/govmomi/vim25/progress" "github.com/vmware/govmomi/vim25/types" ) -type ovfFileItem struct { - url *url.URL - item types.OvfFileItem - ch chan progress.Report +type FileItem struct { + types.OvfFileItem + URL *url.URL + + ch chan progress.Report } -func (o ovfFileItem) Sink() chan<- progress.Report { +func NewFileItem(u *url.URL, item types.OvfFileItem) FileItem { + return FileItem{ + OvfFileItem: item, + URL: u, + ch: make(chan progress.Report), + } +} + +func (o FileItem) Sink() chan<- progress.Report { return o.ch } -type leaseUpdater struct { - client *vim25.Client - lease *object.HttpNfcLease +type LeaseUpdater struct { + lease *Lease pos int64 // Number of bytes total int64 // Total number of bytes @@ -52,16 +58,15 @@ type leaseUpdater struct { wg sync.WaitGroup // Track when update loop is done } -func newLeaseUpdater(client *vim25.Client, lease *object.HttpNfcLease, items []ovfFileItem) *leaseUpdater { - l := leaseUpdater{ - client: client, - lease: lease, +func newLeaseUpdater(ctx context.Context, lease *Lease, info *LeaseInfo) *LeaseUpdater { + l := LeaseUpdater{ + lease: lease, done: make(chan struct{}), } - for _, item := range items { - l.total += item.item.Size + for _, item := range info.Items { + l.total += item.Size go l.waitForProgress(item) } @@ -72,10 +77,10 @@ func newLeaseUpdater(client *vim25.Client, lease *object.HttpNfcLease, items []o return &l } -func (l *leaseUpdater) waitForProgress(item ovfFileItem) { +func (l *LeaseUpdater) waitForProgress(item FileItem) { var pos, total int64 - total = item.item.Size + total = item.Size for { select { @@ -101,7 +106,7 @@ func (l *leaseUpdater) waitForProgress(item ovfFileItem) { } } -func (l *leaseUpdater) run() { +func (l *LeaseUpdater) run() { defer l.wg.Done() tick := time.NewTicker(2 * time.Second) @@ -117,15 +122,16 @@ func (l *leaseUpdater) run() { // Always report the current value of percent, as it will renew the // lease even if the value hasn't changed or is 0. percent := int32(float32(100*atomic.LoadInt64(&l.pos)) / float32(l.total)) - err := l.lease.HttpNfcLeaseProgress(context.TODO(), percent) + err := l.lease.Progress(context.TODO(), percent) if err != nil { - fmt.Printf("from lease updater: %s\n", err) + log.Printf("NFC lease progress: %s", err) + return } } } } -func (l *leaseUpdater) Done() { +func (l *LeaseUpdater) Done() { close(l.done) l.wg.Wait() } diff --git a/vendor/github.com/vmware/govmomi/object/authorization_manager_internal.go b/vendor/github.com/vmware/govmomi/object/authorization_manager_internal.go new file mode 100644 index 00000000..4fa520f5 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/object/authorization_manager_internal.go @@ -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) +} diff --git a/vendor/github.com/vmware/govmomi/object/common.go b/vendor/github.com/vmware/govmomi/object/common.go index 52feeed6..8f0a94ff 100644 --- a/vendor/github.com/vmware/govmomi/object/common.go +++ b/vendor/github.com/vmware/govmomi/object/common.go @@ -80,11 +80,6 @@ func (c *Common) SetInventoryPath(p string) { func (c Common) ObjectName(ctx context.Context) (string, error) { var o mo.ManagedEntity - name := c.Name() - if name != "" { - return name, nil - } - err := c.Properties(ctx, c.Reference(), []string{"name"}, &o) if err != nil { return "", err diff --git a/vendor/github.com/vmware/govmomi/object/datastore_file.go b/vendor/github.com/vmware/govmomi/object/datastore_file.go index 1306ef6d..a73990f1 100644 --- a/vendor/github.com/vmware/govmomi/object/datastore_file.go +++ b/vendor/github.com/vmware/govmomi/object/datastore_file.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2016 VMware, Inc. All Rights Reserved. +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. @@ -25,6 +25,7 @@ import ( "net/http" "os" "path" + "sync" "time" "github.com/vmware/govmomi/vim25/soap" @@ -232,8 +233,9 @@ func (f *DatastoreFile) get() (io.Reader, error) { return f.body, nil } -func lastIndexLines(s []byte, n *int) int64 { +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') @@ -241,18 +243,27 @@ func lastIndexLines(s []byte, n *int) int64 { break } - i = o - *n-- - if *n == 0 { + msg := string(s[o+1 : i+1]) + if !include(*line, msg) { + done = true break + } else { + i = o + *line++ } } - return int64(i) + 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) @@ -261,13 +272,14 @@ func (f *DatastoreFile) Tail(n int) error { return err } - if n == 0 { + if lines == 0 { return nil } chunk := int64(-1) buf := bytes.NewBuffer(make([]byte, 0, bsize)) + line := 0 for { var eof bool @@ -298,19 +310,19 @@ func (f *DatastoreFile) Tail(n int) error { } b := buf.Bytes() - idx := lastIndexLines(b, &n) + 1 + idx, done := lastIndexLines(b, &line, include) - if n == 0 { + 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)) + buf.Next(int(idx + 1)) f.buf = buf return nil } - if _, err = f.Seek(pos+idx, io.SeekStart); err != nil { + if _, err = f.Seek(pos+idx+1, io.SeekStart); err != nil { return err } @@ -336,6 +348,7 @@ type followDatastoreFile struct { r *DatastoreFile c chan struct{} i time.Duration + o sync.Once } // Read reads up to len(b) bytes from the DatastoreFile being followed. @@ -344,8 +357,6 @@ func (f *followDatastoreFile) Read(p []byte) (int, error) { offset := f.r.offset.seek stop := false - defer f.r.Close() - for { n, err := f.r.Read(p) if err != nil && err == io.EOF { @@ -389,11 +400,15 @@ func (f *followDatastoreFile) Read(p []byte) (int, error) { // Close will stop Follow polling and close the underlying DatastoreFile. func (f *followDatastoreFile) Close() error { - close(f.c) + f.o.Do(func() { 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} + return &followDatastoreFile{ + r: f, + c: make(chan struct{}), + i: interval, + } } diff --git a/vendor/github.com/vmware/govmomi/object/datastore_file_manager.go b/vendor/github.com/vmware/govmomi/object/datastore_file_manager.go new file mode 100644 index 00000000..e4843680 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/object/datastore_file_manager.go @@ -0,0 +1,164 @@ +/* +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) +} + +// Move dispatches to the appropriate Move method based on file name extension +func (m *DatastoreFileManager) Move(ctx context.Context, src string, dst string) error { + srcp := m.Path(src) + dstp := m.Path(dst) + + f := m.FileManager.MoveDatastoreFile + + if srcp.IsVMDK() { + f = m.VirtualDiskManager.MoveVirtualDisk + } + + task, err := f(ctx, srcp.String(), m.Datacenter, dstp.String(), m.Datacenter, m.Force) + if err != nil { + 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) +} diff --git a/vendor/github.com/vmware/govmomi/object/datastore_path.go b/vendor/github.com/vmware/govmomi/object/datastore_path.go index ea152103..1563ee1e 100644 --- a/vendor/github.com/vmware/govmomi/object/datastore_path.go +++ b/vendor/github.com/vmware/govmomi/object/datastore_path.go @@ -18,6 +18,7 @@ package object import ( "fmt" + "path" "strings" ) @@ -63,3 +64,8 @@ func (p *DatastorePath) String() string { return strings.Join([]string{s, p.Path}, " ") } + +// IsVMDK returns true if Path has a ".vmdk" extension +func (p *DatastorePath) IsVMDK() bool { + return path.Ext(p.Path) == ".vmdk" +} diff --git a/vendor/github.com/vmware/govmomi/object/host_config_manager.go b/vendor/github.com/vmware/govmomi/object/host_config_manager.go index 6f061a6d..123227ec 100644 --- a/vendor/github.com/vmware/govmomi/object/host_config_manager.go +++ b/vendor/github.com/vmware/govmomi/object/host_config_manager.go @@ -105,6 +105,22 @@ func (m HostConfigManager) VsanSystem(ctx context.Context) (*HostVsanSystem, err 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 diff --git a/vendor/github.com/vmware/govmomi/object/host_vsan_internal_system.go b/vendor/github.com/vmware/govmomi/object/host_vsan_internal_system.go new file mode 100644 index 00000000..65e4587f --- /dev/null +++ b/vendor/github.com/vmware/govmomi/object/host_vsan_internal_system.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/object/http_nfc_lease.go b/vendor/github.com/vmware/govmomi/object/http_nfc_lease.go deleted file mode 100644 index 3ca53558..00000000 --- a/vendor/github.com/vmware/govmomi/object/http_nfc_lease.go +++ /dev/null @@ -1,143 +0,0 @@ -/* -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) -} diff --git a/vendor/github.com/vmware/govmomi/object/list_view.go b/vendor/github.com/vmware/govmomi/object/list_view.go deleted file mode 100644 index 8c755427..00000000 --- a/vendor/github.com/vmware/govmomi/object/list_view.go +++ /dev/null @@ -1,70 +0,0 @@ -/* -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 ListView struct { - Common -} - -func NewListView(c *vim25.Client, ref types.ManagedObjectReference) *ListView { - return &ListView{ - Common: NewCommon(c, ref), - } -} - -func (v ListView) Destroy(ctx context.Context) error { - req := types.DestroyView{ - This: v.Reference(), - } - _, err := methods.DestroyView(ctx, v.c, &req) - return err -} - -func (v ListView) Add(ctx context.Context, refs []types.ManagedObjectReference) error { - req := types.ModifyListView{ - This: v.Reference(), - Add: refs, - } - _, err := methods.ModifyListView(ctx, v.c, &req) - return err -} - -func (v ListView) Remove(ctx context.Context, refs []types.ManagedObjectReference) error { - req := types.ModifyListView{ - This: v.Reference(), - Remove: refs, - } - _, err := methods.ModifyListView(ctx, v.c, &req) - return err -} - -func (v ListView) Reset(ctx context.Context, refs []types.ManagedObjectReference) error { - req := types.ResetListView{ - This: v.Reference(), - Obj: refs, - } - _, err := methods.ResetListView(ctx, v.c, &req) - return err -} diff --git a/vendor/github.com/vmware/govmomi/object/network.go b/vendor/github.com/vmware/govmomi/object/network.go index a76b17d9..d1dc7ce0 100644 --- a/vendor/github.com/vmware/govmomi/object/network.go +++ b/vendor/github.com/vmware/govmomi/object/network.go @@ -20,6 +20,7 @@ import ( "context" "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" ) @@ -34,12 +35,20 @@ func NewNetwork(c *vim25.Client, ref types.ManagedObjectReference) *Network { } // EthernetCardBackingInfo returns the VirtualDeviceBackingInfo for this Network -func (n Network) EthernetCardBackingInfo(_ context.Context) (types.BaseVirtualDeviceBackingInfo, error) { - name := n.Name() +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: name, + DeviceName: e.Name, }, } diff --git a/vendor/github.com/vmware/govmomi/object/opaque_network.go b/vendor/github.com/vmware/govmomi/object/opaque_network.go new file mode 100644 index 00000000..47ce4cef --- /dev/null +++ b/vendor/github.com/vmware/govmomi/object/opaque_network.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/object/resource_pool.go b/vendor/github.com/vmware/govmomi/object/resource_pool.go index 791fd382..55c2e2b2 100644 --- a/vendor/github.com/vmware/govmomi/object/resource_pool.go +++ b/vendor/github.com/vmware/govmomi/object/resource_pool.go @@ -19,6 +19,7 @@ package object import ( "context" + "github.com/vmware/govmomi/nfc" "github.com/vmware/govmomi/vim25" "github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/types" @@ -34,7 +35,7 @@ func NewResourcePool(c *vim25.Client, ref types.ManagedObjectReference) *Resourc } } -func (p ResourcePool) ImportVApp(ctx context.Context, spec types.BaseImportSpec, folder *Folder, host *HostSystem) (*HttpNfcLease, error) { +func (p ResourcePool) ImportVApp(ctx context.Context, spec types.BaseImportSpec, folder *Folder, host *HostSystem) (*nfc.Lease, error) { req := types.ImportVApp{ This: p.Reference(), Spec: spec, @@ -55,7 +56,7 @@ func (p ResourcePool) ImportVApp(ctx context.Context, spec types.BaseImportSpec, return nil, err } - return NewHttpNfcLease(p.c, res.Returnval), nil + return nfc.NewLease(p.c, res.Returnval), nil } func (p ResourcePool) Create(ctx context.Context, name string, spec types.ResourceConfigSpec) (*ResourcePool, error) { diff --git a/vendor/github.com/vmware/govmomi/object/task.go b/vendor/github.com/vmware/govmomi/object/task.go index 8572b436..2b66aa93 100644 --- a/vendor/github.com/vmware/govmomi/object/task.go +++ b/vendor/github.com/vmware/govmomi/object/task.go @@ -22,6 +22,7 @@ import ( "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/task" "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/progress" "github.com/vmware/govmomi/vim25/types" ) @@ -51,3 +52,11 @@ func (t *Task) WaitForResult(ctx context.Context, s progress.Sinker) (*types.Tas p := property.DefaultCollector(t.c) return task.Wait(ctx, t.Reference(), p, s) } + +func (t *Task) Cancel(ctx context.Context) error { + _, err := methods.CancelTask(ctx, t.Client(), &types.CancelTask{ + This: t.Reference(), + }) + + return err +} diff --git a/vendor/github.com/vmware/govmomi/object/virtual_disk_manager_internal.go b/vendor/github.com/vmware/govmomi/object/virtual_disk_manager_internal.go new file mode 100644 index 00000000..642cd62f --- /dev/null +++ b/vendor/github.com/vmware/govmomi/object/virtual_disk_manager_internal.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/object/virtual_machine.go b/vendor/github.com/vmware/govmomi/object/virtual_machine.go index 87ae9a51..02c4e237 100644 --- a/vendor/github.com/vmware/govmomi/object/virtual_machine.go +++ b/vendor/github.com/vmware/govmomi/object/virtual_machine.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2015 VMware, Inc. All Rights Reserved. +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. @@ -518,11 +518,11 @@ func (m snapshotMap) add(parent string, tree []types.VirtualMachineSnapshotTree) } } -// findSnapshot supports snapshot lookup by name, where name can be: +// 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) { +func (v VirtualMachine) FindSnapshot(ctx context.Context, name string) (Reference, error) { var o mo.VirtualMachine err := v.Properties(ctx, v.Reference(), []string{"snapshot"}, &o) @@ -550,7 +550,7 @@ func (v VirtualMachine) findSnapshot(ctx context.Context, name string) (Referenc // 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) + snapshot, err := v.FindSnapshot(ctx, name) if err != nil { return nil, err } @@ -586,7 +586,7 @@ func (v VirtualMachine) RevertToCurrentSnapshot(ctx context.Context, suppressPow // 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) + snapshot, err := v.FindSnapshot(ctx, name) if err != nil { return nil, err } @@ -725,3 +725,35 @@ func (v VirtualMachine) QueryConfigTarget(ctx context.Context) (*types.ConfigTar 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 +} diff --git a/vendor/github.com/vmware/govmomi/object/ovf_manager.go b/vendor/github.com/vmware/govmomi/ovf/manager.go similarity index 55% rename from vendor/github.com/vmware/govmomi/object/ovf_manager.go rename to vendor/github.com/vmware/govmomi/ovf/manager.go index 7fedf689..3ee2afdd 100644 --- a/vendor/github.com/vmware/govmomi/object/ovf_manager.go +++ b/vendor/github.com/vmware/govmomi/ovf/manager.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2015 VMware, Inc. All Rights Reserved. +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. @@ -14,37 +14,36 @@ See the License for the specific language governing permissions and limitations under the License. */ -package object +package ovf 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 OvfManager struct { - Common -} +type Manager struct { + types.ManagedObjectReference -func NewOvfManager(c *vim25.Client) *OvfManager { - o := OvfManager{ - Common: NewCommon(c, *c.ServiceContent.OvfManager), - } + c *vim25.Client +} - return &o +func NewManager(c *vim25.Client) *Manager { + return &Manager{*c.ServiceContent.OvfManager, c} } // CreateDescriptor wraps methods.CreateDescriptor -func (o OvfManager) CreateDescriptor(ctx context.Context, obj Reference, cdp types.OvfCreateDescriptorParams) (*types.OvfCreateDescriptorResult, error) { +func (m *Manager) CreateDescriptor(ctx context.Context, obj mo.Reference, cdp types.OvfCreateDescriptorParams) (*types.OvfCreateDescriptorResult, error) { req := types.CreateDescriptor{ - This: o.Reference(), + This: m.Reference(), Obj: obj.Reference(), Cdp: cdp, } - res, err := methods.CreateDescriptor(ctx, o.c, &req) + res, err := methods.CreateDescriptor(ctx, m.c, &req) if err != nil { return nil, err } @@ -53,16 +52,16 @@ func (o OvfManager) CreateDescriptor(ctx context.Context, obj Reference, cdp typ } // CreateImportSpec wraps methods.CreateImportSpec -func (o OvfManager) CreateImportSpec(ctx context.Context, ovfDescriptor string, resourcePool Reference, datastore Reference, cisp types.OvfCreateImportSpecParams) (*types.OvfCreateImportSpecResult, error) { +func (m *Manager) CreateImportSpec(ctx context.Context, ovfDescriptor string, resourcePool mo.Reference, datastore mo.Reference, cisp types.OvfCreateImportSpecParams) (*types.OvfCreateImportSpecResult, error) { req := types.CreateImportSpec{ - This: o.Reference(), + This: m.Reference(), OvfDescriptor: ovfDescriptor, ResourcePool: resourcePool.Reference(), Datastore: datastore.Reference(), Cisp: cisp, } - res, err := methods.CreateImportSpec(ctx, o.c, &req) + res, err := methods.CreateImportSpec(ctx, m.c, &req) if err != nil { return nil, err } @@ -71,14 +70,14 @@ func (o OvfManager) CreateImportSpec(ctx context.Context, ovfDescriptor string, } // ParseDescriptor wraps methods.ParseDescriptor -func (o OvfManager) ParseDescriptor(ctx context.Context, ovfDescriptor string, pdp types.OvfParseDescriptorParams) (*types.OvfParseDescriptorResult, error) { +func (m *Manager) ParseDescriptor(ctx context.Context, ovfDescriptor string, pdp types.OvfParseDescriptorParams) (*types.OvfParseDescriptorResult, error) { req := types.ParseDescriptor{ - This: o.Reference(), + This: m.Reference(), OvfDescriptor: ovfDescriptor, Pdp: pdp, } - res, err := methods.ParseDescriptor(ctx, o.c, &req) + res, err := methods.ParseDescriptor(ctx, m.c, &req) if err != nil { return nil, err } @@ -87,15 +86,15 @@ func (o OvfManager) ParseDescriptor(ctx context.Context, ovfDescriptor string, p } // ValidateHost wraps methods.ValidateHost -func (o OvfManager) ValidateHost(ctx context.Context, ovfDescriptor string, host Reference, vhp types.OvfValidateHostParams) (*types.OvfValidateHostResult, error) { +func (m *Manager) ValidateHost(ctx context.Context, ovfDescriptor string, host mo.Reference, vhp types.OvfValidateHostParams) (*types.OvfValidateHostResult, error) { req := types.ValidateHost{ - This: o.Reference(), + This: m.Reference(), OvfDescriptor: ovfDescriptor, Host: host.Reference(), Vhp: vhp, } - res, err := methods.ValidateHost(ctx, o.c, &req) + res, err := methods.ValidateHost(ctx, m.c, &req) if err != nil { return nil, err } diff --git a/vendor/github.com/vmware/govmomi/pbm/client.go b/vendor/github.com/vmware/govmomi/pbm/client.go new file mode 100644 index 00000000..a0f07fc9 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/pbm/client.go @@ -0,0 +1,217 @@ +/* +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 pbm + +import ( + "context" + "fmt" + + "github.com/vmware/govmomi/pbm/methods" + "github.com/vmware/govmomi/pbm/types" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/soap" + vim "github.com/vmware/govmomi/vim25/types" +) + +type Client struct { + *soap.Client + + ServiceContent types.PbmServiceInstanceContent +} + +func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) { + sc := c.Client.NewServiceClient("/pbm/sdk", "urn:pbm") + + req := types.PbmRetrieveServiceContent{ + This: vim.ManagedObjectReference{ + Type: "PbmServiceInstance", + Value: "ServiceInstance", + }, + } + + res, err := methods.PbmRetrieveServiceContent(ctx, sc, &req) + if err != nil { + return nil, err + } + + return &Client{sc, res.Returnval}, nil +} + +func (c *Client) QueryProfile(ctx context.Context, rtype types.PbmProfileResourceType, category string) ([]types.PbmProfileId, error) { + req := types.PbmQueryProfile{ + This: c.ServiceContent.ProfileManager, + ResourceType: rtype, + ProfileCategory: category, + } + + res, err := methods.PbmQueryProfile(ctx, c, &req) + if err != nil { + return nil, err + } + + return res.Returnval, nil +} + +func (c *Client) RetrieveContent(ctx context.Context, ids []types.PbmProfileId) ([]types.BasePbmProfile, error) { + req := types.PbmRetrieveContent{ + This: c.ServiceContent.ProfileManager, + ProfileIds: ids, + } + + res, err := methods.PbmRetrieveContent(ctx, c, &req) + if err != nil { + return nil, err + } + + return res.Returnval, nil +} + +type PlacementCompatibilityResult []types.PbmPlacementCompatibilityResult + +func (c *Client) CheckRequirements(ctx context.Context, hubs []types.PbmPlacementHub, ref *types.PbmServerObjectRef, preq []types.BasePbmPlacementRequirement) (PlacementCompatibilityResult, error) { + req := types.PbmCheckRequirements{ + This: c.ServiceContent.PlacementSolver, + HubsToSearch: hubs, + PlacementSubjectRef: ref, + PlacementSubjectRequirement: preq, + } + + res, err := methods.PbmCheckRequirements(ctx, c, &req) + if err != nil { + return nil, err + } + + return res.Returnval, nil +} + +func (l PlacementCompatibilityResult) CompatibleDatastores() []types.PbmPlacementHub { + var compatibleDatastores []types.PbmPlacementHub + + for _, res := range l { + if len(res.Error) == 0 { + compatibleDatastores = append(compatibleDatastores, res.Hub) + } + } + return compatibleDatastores +} + +func (l PlacementCompatibilityResult) NonCompatibleDatastores() []types.PbmPlacementHub { + var nonCompatibleDatastores []types.PbmPlacementHub + + for _, res := range l { + if len(res.Error) > 0 { + nonCompatibleDatastores = append(nonCompatibleDatastores, res.Hub) + } + } + return nonCompatibleDatastores +} + +func (c *Client) CreateProfile(ctx context.Context, capabilityProfileCreateSpec types.PbmCapabilityProfileCreateSpec) (*types.PbmProfileId, error) { + req := types.PbmCreate{ + This: c.ServiceContent.ProfileManager, + CreateSpec: capabilityProfileCreateSpec, + } + + res, err := methods.PbmCreate(ctx, c, &req) + if err != nil { + return nil, err + } + + return &res.Returnval, nil +} + +func (c *Client) UpdateProfile(ctx context.Context, id types.PbmProfileId, updateSpec types.PbmCapabilityProfileUpdateSpec) error { + req := types.PbmUpdate{ + This: c.ServiceContent.ProfileManager, + ProfileId: id, + UpdateSpec: updateSpec, + } + + _, err := methods.PbmUpdate(ctx, c, &req) + if err != nil { + return err + } + + return nil +} + +func (c *Client) DeleteProfile(ctx context.Context, ids []types.PbmProfileId) ([]types.PbmProfileOperationOutcome, error) { + req := types.PbmDelete{ + This: c.ServiceContent.ProfileManager, + ProfileId: ids, + } + + res, err := methods.PbmDelete(ctx, c, &req) + if err != nil { + return nil, err + } + + return res.Returnval, nil +} + +func (c *Client) QueryAssociatedEntity(ctx context.Context, id types.PbmProfileId, entityType string) ([]types.PbmServerObjectRef, error) { + req := types.PbmQueryAssociatedEntity{ + This: c.ServiceContent.ProfileManager, + Profile: id, + EntityType: entityType, + } + + res, err := methods.PbmQueryAssociatedEntity(ctx, c, &req) + if err != nil { + return nil, err + } + + return res.Returnval, nil +} + +func (c *Client) QueryAssociatedEntities(ctx context.Context, ids []types.PbmProfileId) ([]types.PbmQueryProfileResult, error) { + req := types.PbmQueryAssociatedEntities{ + This: c.ServiceContent.ProfileManager, + Profiles: ids, + } + + res, err := methods.PbmQueryAssociatedEntities(ctx, c, &req) + if err != nil { + return nil, err + } + + return res.Returnval, nil +} + +func (c *Client) ProfileIDByName(ctx context.Context, profileName string) (string, error) { + resourceType := types.PbmProfileResourceType{ + ResourceType: string(types.PbmProfileResourceTypeEnumSTORAGE), + } + category := types.PbmProfileCategoryEnumREQUIREMENT + ids, err := c.QueryProfile(ctx, resourceType, string(category)) + if err != nil { + return "", err + } + + profiles, err := c.RetrieveContent(ctx, ids) + if err != nil { + return "", err + } + + for i := range profiles { + profile := profiles[i].GetPbmProfile() + if profile.Name == profileName { + return profile.ProfileId.UniqueId, nil + } + } + return "", fmt.Errorf("no pbm profile found with name: %q", profileName) +} diff --git a/vendor/github.com/vmware/govmomi/pbm/methods/methods.go b/vendor/github.com/vmware/govmomi/pbm/methods/methods.go new file mode 100644 index 00000000..08ee48e9 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/pbm/methods/methods.go @@ -0,0 +1,664 @@ +/* +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 methods + +import ( + "context" + + "github.com/vmware/govmomi/pbm/types" + "github.com/vmware/govmomi/vim25/soap" +) + +type PbmAssignDefaultRequirementProfileBody struct { + Req *types.PbmAssignDefaultRequirementProfile `xml:"urn:pbm PbmAssignDefaultRequirementProfile,omitempty"` + Res *types.PbmAssignDefaultRequirementProfileResponse `xml:"urn:pbm PbmAssignDefaultRequirementProfileResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmAssignDefaultRequirementProfileBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmAssignDefaultRequirementProfile(ctx context.Context, r soap.RoundTripper, req *types.PbmAssignDefaultRequirementProfile) (*types.PbmAssignDefaultRequirementProfileResponse, error) { + var reqBody, resBody PbmAssignDefaultRequirementProfileBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmCheckCompatibilityBody struct { + Req *types.PbmCheckCompatibility `xml:"urn:pbm PbmCheckCompatibility,omitempty"` + Res *types.PbmCheckCompatibilityResponse `xml:"urn:pbm PbmCheckCompatibilityResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmCheckCompatibilityBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmCheckCompatibility(ctx context.Context, r soap.RoundTripper, req *types.PbmCheckCompatibility) (*types.PbmCheckCompatibilityResponse, error) { + var reqBody, resBody PbmCheckCompatibilityBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmCheckCompatibilityWithSpecBody struct { + Req *types.PbmCheckCompatibilityWithSpec `xml:"urn:pbm PbmCheckCompatibilityWithSpec,omitempty"` + Res *types.PbmCheckCompatibilityWithSpecResponse `xml:"urn:pbm PbmCheckCompatibilityWithSpecResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmCheckCompatibilityWithSpecBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmCheckCompatibilityWithSpec(ctx context.Context, r soap.RoundTripper, req *types.PbmCheckCompatibilityWithSpec) (*types.PbmCheckCompatibilityWithSpecResponse, error) { + var reqBody, resBody PbmCheckCompatibilityWithSpecBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmCheckComplianceBody struct { + Req *types.PbmCheckCompliance `xml:"urn:pbm PbmCheckCompliance,omitempty"` + Res *types.PbmCheckComplianceResponse `xml:"urn:pbm PbmCheckComplianceResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmCheckComplianceBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmCheckCompliance(ctx context.Context, r soap.RoundTripper, req *types.PbmCheckCompliance) (*types.PbmCheckComplianceResponse, error) { + var reqBody, resBody PbmCheckComplianceBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmCheckRequirementsBody struct { + Req *types.PbmCheckRequirements `xml:"urn:pbm PbmCheckRequirements,omitempty"` + Res *types.PbmCheckRequirementsResponse `xml:"urn:pbm PbmCheckRequirementsResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmCheckRequirementsBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmCheckRequirements(ctx context.Context, r soap.RoundTripper, req *types.PbmCheckRequirements) (*types.PbmCheckRequirementsResponse, error) { + var reqBody, resBody PbmCheckRequirementsBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmCheckRollupComplianceBody struct { + Req *types.PbmCheckRollupCompliance `xml:"urn:pbm PbmCheckRollupCompliance,omitempty"` + Res *types.PbmCheckRollupComplianceResponse `xml:"urn:pbm PbmCheckRollupComplianceResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmCheckRollupComplianceBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmCheckRollupCompliance(ctx context.Context, r soap.RoundTripper, req *types.PbmCheckRollupCompliance) (*types.PbmCheckRollupComplianceResponse, error) { + var reqBody, resBody PbmCheckRollupComplianceBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmCreateBody struct { + Req *types.PbmCreate `xml:"urn:pbm PbmCreate,omitempty"` + Res *types.PbmCreateResponse `xml:"urn:pbm PbmCreateResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmCreateBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmCreate(ctx context.Context, r soap.RoundTripper, req *types.PbmCreate) (*types.PbmCreateResponse, error) { + var reqBody, resBody PbmCreateBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmDeleteBody struct { + Req *types.PbmDelete `xml:"urn:pbm PbmDelete,omitempty"` + Res *types.PbmDeleteResponse `xml:"urn:pbm PbmDeleteResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmDeleteBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmDelete(ctx context.Context, r soap.RoundTripper, req *types.PbmDelete) (*types.PbmDeleteResponse, error) { + var reqBody, resBody PbmDeleteBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmFetchCapabilityMetadataBody struct { + Req *types.PbmFetchCapabilityMetadata `xml:"urn:pbm PbmFetchCapabilityMetadata,omitempty"` + Res *types.PbmFetchCapabilityMetadataResponse `xml:"urn:pbm PbmFetchCapabilityMetadataResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmFetchCapabilityMetadataBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmFetchCapabilityMetadata(ctx context.Context, r soap.RoundTripper, req *types.PbmFetchCapabilityMetadata) (*types.PbmFetchCapabilityMetadataResponse, error) { + var reqBody, resBody PbmFetchCapabilityMetadataBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmFetchCapabilitySchemaBody struct { + Req *types.PbmFetchCapabilitySchema `xml:"urn:pbm PbmFetchCapabilitySchema,omitempty"` + Res *types.PbmFetchCapabilitySchemaResponse `xml:"urn:pbm PbmFetchCapabilitySchemaResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmFetchCapabilitySchemaBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmFetchCapabilitySchema(ctx context.Context, r soap.RoundTripper, req *types.PbmFetchCapabilitySchema) (*types.PbmFetchCapabilitySchemaResponse, error) { + var reqBody, resBody PbmFetchCapabilitySchemaBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmFetchComplianceResultBody struct { + Req *types.PbmFetchComplianceResult `xml:"urn:pbm PbmFetchComplianceResult,omitempty"` + Res *types.PbmFetchComplianceResultResponse `xml:"urn:pbm PbmFetchComplianceResultResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmFetchComplianceResultBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmFetchComplianceResult(ctx context.Context, r soap.RoundTripper, req *types.PbmFetchComplianceResult) (*types.PbmFetchComplianceResultResponse, error) { + var reqBody, resBody PbmFetchComplianceResultBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmFetchResourceTypeBody struct { + Req *types.PbmFetchResourceType `xml:"urn:pbm PbmFetchResourceType,omitempty"` + Res *types.PbmFetchResourceTypeResponse `xml:"urn:pbm PbmFetchResourceTypeResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmFetchResourceTypeBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmFetchResourceType(ctx context.Context, r soap.RoundTripper, req *types.PbmFetchResourceType) (*types.PbmFetchResourceTypeResponse, error) { + var reqBody, resBody PbmFetchResourceTypeBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmFetchRollupComplianceResultBody struct { + Req *types.PbmFetchRollupComplianceResult `xml:"urn:pbm PbmFetchRollupComplianceResult,omitempty"` + Res *types.PbmFetchRollupComplianceResultResponse `xml:"urn:pbm PbmFetchRollupComplianceResultResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmFetchRollupComplianceResultBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmFetchRollupComplianceResult(ctx context.Context, r soap.RoundTripper, req *types.PbmFetchRollupComplianceResult) (*types.PbmFetchRollupComplianceResultResponse, error) { + var reqBody, resBody PbmFetchRollupComplianceResultBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmFetchVendorInfoBody struct { + Req *types.PbmFetchVendorInfo `xml:"urn:pbm PbmFetchVendorInfo,omitempty"` + Res *types.PbmFetchVendorInfoResponse `xml:"urn:pbm PbmFetchVendorInfoResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmFetchVendorInfoBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmFetchVendorInfo(ctx context.Context, r soap.RoundTripper, req *types.PbmFetchVendorInfo) (*types.PbmFetchVendorInfoResponse, error) { + var reqBody, resBody PbmFetchVendorInfoBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmFindApplicableDefaultProfileBody struct { + Req *types.PbmFindApplicableDefaultProfile `xml:"urn:pbm PbmFindApplicableDefaultProfile,omitempty"` + Res *types.PbmFindApplicableDefaultProfileResponse `xml:"urn:pbm PbmFindApplicableDefaultProfileResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmFindApplicableDefaultProfileBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmFindApplicableDefaultProfile(ctx context.Context, r soap.RoundTripper, req *types.PbmFindApplicableDefaultProfile) (*types.PbmFindApplicableDefaultProfileResponse, error) { + var reqBody, resBody PbmFindApplicableDefaultProfileBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmQueryAssociatedEntitiesBody struct { + Req *types.PbmQueryAssociatedEntities `xml:"urn:pbm PbmQueryAssociatedEntities,omitempty"` + Res *types.PbmQueryAssociatedEntitiesResponse `xml:"urn:pbm PbmQueryAssociatedEntitiesResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmQueryAssociatedEntitiesBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmQueryAssociatedEntities(ctx context.Context, r soap.RoundTripper, req *types.PbmQueryAssociatedEntities) (*types.PbmQueryAssociatedEntitiesResponse, error) { + var reqBody, resBody PbmQueryAssociatedEntitiesBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmQueryAssociatedEntityBody struct { + Req *types.PbmQueryAssociatedEntity `xml:"urn:pbm PbmQueryAssociatedEntity,omitempty"` + Res *types.PbmQueryAssociatedEntityResponse `xml:"urn:pbm PbmQueryAssociatedEntityResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmQueryAssociatedEntityBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmQueryAssociatedEntity(ctx context.Context, r soap.RoundTripper, req *types.PbmQueryAssociatedEntity) (*types.PbmQueryAssociatedEntityResponse, error) { + var reqBody, resBody PbmQueryAssociatedEntityBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmQueryAssociatedProfileBody struct { + Req *types.PbmQueryAssociatedProfile `xml:"urn:pbm PbmQueryAssociatedProfile,omitempty"` + Res *types.PbmQueryAssociatedProfileResponse `xml:"urn:pbm PbmQueryAssociatedProfileResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmQueryAssociatedProfileBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmQueryAssociatedProfile(ctx context.Context, r soap.RoundTripper, req *types.PbmQueryAssociatedProfile) (*types.PbmQueryAssociatedProfileResponse, error) { + var reqBody, resBody PbmQueryAssociatedProfileBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmQueryAssociatedProfilesBody struct { + Req *types.PbmQueryAssociatedProfiles `xml:"urn:pbm PbmQueryAssociatedProfiles,omitempty"` + Res *types.PbmQueryAssociatedProfilesResponse `xml:"urn:pbm PbmQueryAssociatedProfilesResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmQueryAssociatedProfilesBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmQueryAssociatedProfiles(ctx context.Context, r soap.RoundTripper, req *types.PbmQueryAssociatedProfiles) (*types.PbmQueryAssociatedProfilesResponse, error) { + var reqBody, resBody PbmQueryAssociatedProfilesBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmQueryByRollupComplianceStatusBody struct { + Req *types.PbmQueryByRollupComplianceStatus `xml:"urn:pbm PbmQueryByRollupComplianceStatus,omitempty"` + Res *types.PbmQueryByRollupComplianceStatusResponse `xml:"urn:pbm PbmQueryByRollupComplianceStatusResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmQueryByRollupComplianceStatusBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmQueryByRollupComplianceStatus(ctx context.Context, r soap.RoundTripper, req *types.PbmQueryByRollupComplianceStatus) (*types.PbmQueryByRollupComplianceStatusResponse, error) { + var reqBody, resBody PbmQueryByRollupComplianceStatusBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmQueryDefaultRequirementProfileBody struct { + Req *types.PbmQueryDefaultRequirementProfile `xml:"urn:pbm PbmQueryDefaultRequirementProfile,omitempty"` + Res *types.PbmQueryDefaultRequirementProfileResponse `xml:"urn:pbm PbmQueryDefaultRequirementProfileResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmQueryDefaultRequirementProfileBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmQueryDefaultRequirementProfile(ctx context.Context, r soap.RoundTripper, req *types.PbmQueryDefaultRequirementProfile) (*types.PbmQueryDefaultRequirementProfileResponse, error) { + var reqBody, resBody PbmQueryDefaultRequirementProfileBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmQueryDefaultRequirementProfilesBody struct { + Req *types.PbmQueryDefaultRequirementProfiles `xml:"urn:pbm PbmQueryDefaultRequirementProfiles,omitempty"` + Res *types.PbmQueryDefaultRequirementProfilesResponse `xml:"urn:pbm PbmQueryDefaultRequirementProfilesResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmQueryDefaultRequirementProfilesBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmQueryDefaultRequirementProfiles(ctx context.Context, r soap.RoundTripper, req *types.PbmQueryDefaultRequirementProfiles) (*types.PbmQueryDefaultRequirementProfilesResponse, error) { + var reqBody, resBody PbmQueryDefaultRequirementProfilesBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmQueryMatchingHubBody struct { + Req *types.PbmQueryMatchingHub `xml:"urn:pbm PbmQueryMatchingHub,omitempty"` + Res *types.PbmQueryMatchingHubResponse `xml:"urn:pbm PbmQueryMatchingHubResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmQueryMatchingHubBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmQueryMatchingHub(ctx context.Context, r soap.RoundTripper, req *types.PbmQueryMatchingHub) (*types.PbmQueryMatchingHubResponse, error) { + var reqBody, resBody PbmQueryMatchingHubBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmQueryMatchingHubWithSpecBody struct { + Req *types.PbmQueryMatchingHubWithSpec `xml:"urn:pbm PbmQueryMatchingHubWithSpec,omitempty"` + Res *types.PbmQueryMatchingHubWithSpecResponse `xml:"urn:pbm PbmQueryMatchingHubWithSpecResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmQueryMatchingHubWithSpecBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmQueryMatchingHubWithSpec(ctx context.Context, r soap.RoundTripper, req *types.PbmQueryMatchingHubWithSpec) (*types.PbmQueryMatchingHubWithSpecResponse, error) { + var reqBody, resBody PbmQueryMatchingHubWithSpecBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmQueryProfileBody struct { + Req *types.PbmQueryProfile `xml:"urn:pbm PbmQueryProfile,omitempty"` + Res *types.PbmQueryProfileResponse `xml:"urn:pbm PbmQueryProfileResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmQueryProfileBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmQueryProfile(ctx context.Context, r soap.RoundTripper, req *types.PbmQueryProfile) (*types.PbmQueryProfileResponse, error) { + var reqBody, resBody PbmQueryProfileBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmQueryReplicationGroupsBody struct { + Req *types.PbmQueryReplicationGroups `xml:"urn:pbm PbmQueryReplicationGroups,omitempty"` + Res *types.PbmQueryReplicationGroupsResponse `xml:"urn:pbm PbmQueryReplicationGroupsResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmQueryReplicationGroupsBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmQueryReplicationGroups(ctx context.Context, r soap.RoundTripper, req *types.PbmQueryReplicationGroups) (*types.PbmQueryReplicationGroupsResponse, error) { + var reqBody, resBody PbmQueryReplicationGroupsBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmQuerySpaceStatsForStorageContainerBody struct { + Req *types.PbmQuerySpaceStatsForStorageContainer `xml:"urn:pbm PbmQuerySpaceStatsForStorageContainer,omitempty"` + Res *types.PbmQuerySpaceStatsForStorageContainerResponse `xml:"urn:pbm PbmQuerySpaceStatsForStorageContainerResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmQuerySpaceStatsForStorageContainerBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmQuerySpaceStatsForStorageContainer(ctx context.Context, r soap.RoundTripper, req *types.PbmQuerySpaceStatsForStorageContainer) (*types.PbmQuerySpaceStatsForStorageContainerResponse, error) { + var reqBody, resBody PbmQuerySpaceStatsForStorageContainerBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmResetDefaultRequirementProfileBody struct { + Req *types.PbmResetDefaultRequirementProfile `xml:"urn:pbm PbmResetDefaultRequirementProfile,omitempty"` + Res *types.PbmResetDefaultRequirementProfileResponse `xml:"urn:pbm PbmResetDefaultRequirementProfileResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmResetDefaultRequirementProfileBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmResetDefaultRequirementProfile(ctx context.Context, r soap.RoundTripper, req *types.PbmResetDefaultRequirementProfile) (*types.PbmResetDefaultRequirementProfileResponse, error) { + var reqBody, resBody PbmResetDefaultRequirementProfileBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmResetVSanDefaultProfileBody struct { + Req *types.PbmResetVSanDefaultProfile `xml:"urn:pbm PbmResetVSanDefaultProfile,omitempty"` + Res *types.PbmResetVSanDefaultProfileResponse `xml:"urn:pbm PbmResetVSanDefaultProfileResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmResetVSanDefaultProfileBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmResetVSanDefaultProfile(ctx context.Context, r soap.RoundTripper, req *types.PbmResetVSanDefaultProfile) (*types.PbmResetVSanDefaultProfileResponse, error) { + var reqBody, resBody PbmResetVSanDefaultProfileBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmRetrieveContentBody struct { + Req *types.PbmRetrieveContent `xml:"urn:pbm PbmRetrieveContent,omitempty"` + Res *types.PbmRetrieveContentResponse `xml:"urn:pbm PbmRetrieveContentResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmRetrieveContentBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmRetrieveContent(ctx context.Context, r soap.RoundTripper, req *types.PbmRetrieveContent) (*types.PbmRetrieveContentResponse, error) { + var reqBody, resBody PbmRetrieveContentBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmRetrieveServiceContentBody struct { + Req *types.PbmRetrieveServiceContent `xml:"urn:pbm PbmRetrieveServiceContent,omitempty"` + Res *types.PbmRetrieveServiceContentResponse `xml:"urn:pbm PbmRetrieveServiceContentResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmRetrieveServiceContentBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmRetrieveServiceContent(ctx context.Context, r soap.RoundTripper, req *types.PbmRetrieveServiceContent) (*types.PbmRetrieveServiceContentResponse, error) { + var reqBody, resBody PbmRetrieveServiceContentBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} + +type PbmUpdateBody struct { + Req *types.PbmUpdate `xml:"urn:pbm PbmUpdate,omitempty"` + Res *types.PbmUpdateResponse `xml:"urn:pbm PbmUpdateResponse,omitempty"` + Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *PbmUpdateBody) Fault() *soap.Fault { return b.Fault_ } + +func PbmUpdate(ctx context.Context, r soap.RoundTripper, req *types.PbmUpdate) (*types.PbmUpdateResponse, error) { + var reqBody, resBody PbmUpdateBody + + reqBody.Req = req + + if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { + return nil, err + } + + return resBody.Res, nil +} diff --git a/vendor/github.com/vmware/govmomi/pbm/pbm_util.go b/vendor/github.com/vmware/govmomi/pbm/pbm_util.go new file mode 100644 index 00000000..4cc8085f --- /dev/null +++ b/vendor/github.com/vmware/govmomi/pbm/pbm_util.go @@ -0,0 +1,146 @@ +/* +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 pbm + +import ( + "fmt" + "strconv" + "strings" + + "github.com/vmware/govmomi/pbm/types" +) + +// A struct to capture pbm create spec details. +type CapabilityProfileCreateSpec struct { + Name string + Description string + Category string + CapabilityList []Capability +} + +// A struct to capture pbm capability instance details. +type Capability struct { + ID string + Namespace string + PropertyList []Property +} + +// A struct to capture pbm property instance details. +type Property struct { + ID string + Operator string + Value string + DataType string +} + +func CreateCapabilityProfileSpec(pbmCreateSpec CapabilityProfileCreateSpec) (*types.PbmCapabilityProfileCreateSpec, error) { + capabilities, err := createCapabilityInstances(pbmCreateSpec.CapabilityList) + if err != nil { + return nil, err + } + + pbmCapabilityProfileSpec := types.PbmCapabilityProfileCreateSpec{ + Name: pbmCreateSpec.Name, + Description: pbmCreateSpec.Description, + Category: pbmCreateSpec.Category, + ResourceType: types.PbmProfileResourceType{ + ResourceType: string(types.PbmProfileResourceTypeEnumSTORAGE), + }, + Constraints: &types.PbmCapabilitySubProfileConstraints{ + SubProfiles: []types.PbmCapabilitySubProfile{ + types.PbmCapabilitySubProfile{ + Capability: capabilities, + }, + }, + }, + } + return &pbmCapabilityProfileSpec, nil +} + +func createCapabilityInstances(rules []Capability) ([]types.PbmCapabilityInstance, error) { + var capabilityInstances []types.PbmCapabilityInstance + for _, capabilityRule := range rules { + capability := types.PbmCapabilityInstance{ + Id: types.PbmCapabilityMetadataUniqueId{ + Namespace: capabilityRule.Namespace, + Id: capabilityRule.ID, + }, + } + + var propertyInstances []types.PbmCapabilityPropertyInstance + for _, propertyRule := range capabilityRule.PropertyList { + property := types.PbmCapabilityPropertyInstance{ + Id: propertyRule.ID, + } + if propertyRule.Operator != "" { + property.Operator = propertyRule.Operator + } + var err error + switch strings.ToLower(propertyRule.DataType) { + case "int": + // Go int32 is marshalled to xsi:int whereas Go int is marshalled to xsi:long when sending down the wire. + var val int32 + val, err = verifyPropertyValueIsInt(propertyRule.Value, propertyRule.DataType) + property.Value = val + case "bool": + var val bool + val, err = verifyPropertyValueIsBoolean(propertyRule.Value, propertyRule.DataType) + property.Value = val + case "string": + property.Value = propertyRule.Value + case "set": + set := types.PbmCapabilityDiscreteSet{} + for _, val := range strings.Split(propertyRule.Value, ",") { + set.Values = append(set.Values, val) + } + property.Value = set + default: + return nil, fmt.Errorf("invalid value: %q with datatype: %q", propertyRule.Value, propertyRule.Value) + } + if err != nil { + return nil, fmt.Errorf("invalid value: %q with datatype: %q", propertyRule.Value, propertyRule.Value) + } + propertyInstances = append(propertyInstances, property) + } + constraintInstances := []types.PbmCapabilityConstraintInstance{ + types.PbmCapabilityConstraintInstance{ + PropertyInstance: propertyInstances, + }, + } + capability.Constraint = constraintInstances + capabilityInstances = append(capabilityInstances, capability) + } + return capabilityInstances, nil +} + +// Verify if the capability value is of type integer. +func verifyPropertyValueIsInt(propertyValue string, dataType string) (int32, error) { + val, err := strconv.ParseInt(propertyValue, 10, 32) + if err != nil { + return -1, err + } + return int32(val), nil +} + +// Verify if the capability value is of type integer. +func verifyPropertyValueIsBoolean(propertyValue string, dataType string) (bool, error) { + val, err := strconv.ParseBool(propertyValue) + if err != nil { + return false, err + } + return val, nil +} diff --git a/vendor/github.com/vmware/govmomi/pbm/types/enum.go b/vendor/github.com/vmware/govmomi/pbm/types/enum.go new file mode 100644 index 00000000..7cee7514 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/pbm/types/enum.go @@ -0,0 +1,211 @@ +/* +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 types + +import ( + "reflect" + + "github.com/vmware/govmomi/vim25/types" +) + +type PbmBuiltinGenericType string + +const ( + PbmBuiltinGenericTypeVMW_RANGE = PbmBuiltinGenericType("VMW_RANGE") + PbmBuiltinGenericTypeVMW_SET = PbmBuiltinGenericType("VMW_SET") +) + +func init() { + types.Add("pbm:PbmBuiltinGenericType", reflect.TypeOf((*PbmBuiltinGenericType)(nil)).Elem()) +} + +type PbmBuiltinType string + +const ( + PbmBuiltinTypeXSD_LONG = PbmBuiltinType("XSD_LONG") + PbmBuiltinTypeXSD_SHORT = PbmBuiltinType("XSD_SHORT") + PbmBuiltinTypeXSD_INTEGER = PbmBuiltinType("XSD_INTEGER") + PbmBuiltinTypeXSD_INT = PbmBuiltinType("XSD_INT") + PbmBuiltinTypeXSD_STRING = PbmBuiltinType("XSD_STRING") + PbmBuiltinTypeXSD_BOOLEAN = PbmBuiltinType("XSD_BOOLEAN") + PbmBuiltinTypeXSD_DOUBLE = PbmBuiltinType("XSD_DOUBLE") + PbmBuiltinTypeXSD_DATETIME = PbmBuiltinType("XSD_DATETIME") + PbmBuiltinTypeVMW_TIMESPAN = PbmBuiltinType("VMW_TIMESPAN") + PbmBuiltinTypeVMW_POLICY = PbmBuiltinType("VMW_POLICY") +) + +func init() { + types.Add("pbm:PbmBuiltinType", reflect.TypeOf((*PbmBuiltinType)(nil)).Elem()) +} + +type PbmCapabilityOperator string + +const ( + PbmCapabilityOperatorNOT = PbmCapabilityOperator("NOT") +) + +func init() { + types.Add("pbm:PbmCapabilityOperator", reflect.TypeOf((*PbmCapabilityOperator)(nil)).Elem()) +} + +type PbmCapabilityTimeUnitType string + +const ( + PbmCapabilityTimeUnitTypeSECONDS = PbmCapabilityTimeUnitType("SECONDS") + PbmCapabilityTimeUnitTypeMINUTES = PbmCapabilityTimeUnitType("MINUTES") + PbmCapabilityTimeUnitTypeHOURS = PbmCapabilityTimeUnitType("HOURS") + PbmCapabilityTimeUnitTypeDAYS = PbmCapabilityTimeUnitType("DAYS") + PbmCapabilityTimeUnitTypeWEEKS = PbmCapabilityTimeUnitType("WEEKS") + PbmCapabilityTimeUnitTypeMONTHS = PbmCapabilityTimeUnitType("MONTHS") + PbmCapabilityTimeUnitTypeYEARS = PbmCapabilityTimeUnitType("YEARS") +) + +func init() { + types.Add("pbm:PbmCapabilityTimeUnitType", reflect.TypeOf((*PbmCapabilityTimeUnitType)(nil)).Elem()) +} + +type PbmComplianceResultComplianceTaskStatus string + +const ( + PbmComplianceResultComplianceTaskStatusInProgress = PbmComplianceResultComplianceTaskStatus("inProgress") + PbmComplianceResultComplianceTaskStatusSuccess = PbmComplianceResultComplianceTaskStatus("success") + PbmComplianceResultComplianceTaskStatusFailed = PbmComplianceResultComplianceTaskStatus("failed") +) + +func init() { + types.Add("pbm:PbmComplianceResultComplianceTaskStatus", reflect.TypeOf((*PbmComplianceResultComplianceTaskStatus)(nil)).Elem()) +} + +type PbmComplianceStatus string + +const ( + PbmComplianceStatusCompliant = PbmComplianceStatus("compliant") + PbmComplianceStatusNonCompliant = PbmComplianceStatus("nonCompliant") + PbmComplianceStatusUnknown = PbmComplianceStatus("unknown") + PbmComplianceStatusNotApplicable = PbmComplianceStatus("notApplicable") + PbmComplianceStatusOutOfDate = PbmComplianceStatus("outOfDate") +) + +func init() { + types.Add("pbm:PbmComplianceStatus", reflect.TypeOf((*PbmComplianceStatus)(nil)).Elem()) +} + +type PbmIofilterInfoFilterType string + +const ( + PbmIofilterInfoFilterTypeINSPECTION = PbmIofilterInfoFilterType("INSPECTION") + PbmIofilterInfoFilterTypeCOMPRESSION = PbmIofilterInfoFilterType("COMPRESSION") + PbmIofilterInfoFilterTypeENCRYPTION = PbmIofilterInfoFilterType("ENCRYPTION") + PbmIofilterInfoFilterTypeREPLICATION = PbmIofilterInfoFilterType("REPLICATION") + PbmIofilterInfoFilterTypeCACHE = PbmIofilterInfoFilterType("CACHE") + PbmIofilterInfoFilterTypeDATAPROVIDER = PbmIofilterInfoFilterType("DATAPROVIDER") + PbmIofilterInfoFilterTypeDATASTOREIOCONTROL = PbmIofilterInfoFilterType("DATASTOREIOCONTROL") +) + +func init() { + types.Add("pbm:PbmIofilterInfoFilterType", reflect.TypeOf((*PbmIofilterInfoFilterType)(nil)).Elem()) +} + +type PbmLineOfServiceInfoLineOfServiceEnum string + +const ( + PbmLineOfServiceInfoLineOfServiceEnumINSPECTION = PbmLineOfServiceInfoLineOfServiceEnum("INSPECTION") + PbmLineOfServiceInfoLineOfServiceEnumCOMPRESSION = PbmLineOfServiceInfoLineOfServiceEnum("COMPRESSION") + PbmLineOfServiceInfoLineOfServiceEnumENCRYPTION = PbmLineOfServiceInfoLineOfServiceEnum("ENCRYPTION") + PbmLineOfServiceInfoLineOfServiceEnumREPLICATION = PbmLineOfServiceInfoLineOfServiceEnum("REPLICATION") + PbmLineOfServiceInfoLineOfServiceEnumCACHING = PbmLineOfServiceInfoLineOfServiceEnum("CACHING") + PbmLineOfServiceInfoLineOfServiceEnumPERSISTENCE = PbmLineOfServiceInfoLineOfServiceEnum("PERSISTENCE") + PbmLineOfServiceInfoLineOfServiceEnumDATA_PROVIDER = PbmLineOfServiceInfoLineOfServiceEnum("DATA_PROVIDER") + PbmLineOfServiceInfoLineOfServiceEnumDATASTORE_IO_CONTROL = PbmLineOfServiceInfoLineOfServiceEnum("DATASTORE_IO_CONTROL") +) + +func init() { + types.Add("pbm:PbmLineOfServiceInfoLineOfServiceEnum", reflect.TypeOf((*PbmLineOfServiceInfoLineOfServiceEnum)(nil)).Elem()) +} + +type PbmObjectType string + +const ( + PbmObjectTypeVirtualMachine = PbmObjectType("virtualMachine") + PbmObjectTypeVirtualMachineAndDisks = PbmObjectType("virtualMachineAndDisks") + PbmObjectTypeVirtualDiskId = PbmObjectType("virtualDiskId") + PbmObjectTypeVirtualDiskUUID = PbmObjectType("virtualDiskUUID") + PbmObjectTypeDatastore = PbmObjectType("datastore") + PbmObjectTypeUnknown = PbmObjectType("unknown") +) + +func init() { + types.Add("pbm:PbmObjectType", reflect.TypeOf((*PbmObjectType)(nil)).Elem()) +} + +type PbmProfileCategoryEnum string + +const ( + PbmProfileCategoryEnumREQUIREMENT = PbmProfileCategoryEnum("REQUIREMENT") + PbmProfileCategoryEnumRESOURCE = PbmProfileCategoryEnum("RESOURCE") + PbmProfileCategoryEnumDATA_SERVICE_POLICY = PbmProfileCategoryEnum("DATA_SERVICE_POLICY") +) + +func init() { + types.Add("pbm:PbmProfileCategoryEnum", reflect.TypeOf((*PbmProfileCategoryEnum)(nil)).Elem()) +} + +type PbmProfileResourceTypeEnum string + +const ( + PbmProfileResourceTypeEnumSTORAGE = PbmProfileResourceTypeEnum("STORAGE") +) + +func init() { + types.Add("pbm:PbmProfileResourceTypeEnum", reflect.TypeOf((*PbmProfileResourceTypeEnum)(nil)).Elem()) +} + +type PbmSystemCreatedProfileType string + +const ( + PbmSystemCreatedProfileTypeVsanDefaultProfile = PbmSystemCreatedProfileType("VsanDefaultProfile") + PbmSystemCreatedProfileTypeVVolDefaultProfile = PbmSystemCreatedProfileType("VVolDefaultProfile") +) + +func init() { + types.Add("pbm:PbmSystemCreatedProfileType", reflect.TypeOf((*PbmSystemCreatedProfileType)(nil)).Elem()) +} + +type PbmVmOperation string + +const ( + PbmVmOperationCREATE = PbmVmOperation("CREATE") + PbmVmOperationRECONFIGURE = PbmVmOperation("RECONFIGURE") + PbmVmOperationMIGRATE = PbmVmOperation("MIGRATE") + PbmVmOperationCLONE = PbmVmOperation("CLONE") +) + +func init() { + types.Add("pbm:PbmVmOperation", reflect.TypeOf((*PbmVmOperation)(nil)).Elem()) +} + +type PbmVvolType string + +const ( + PbmVvolTypeConfig = PbmVvolType("Config") + PbmVvolTypeData = PbmVvolType("Data") + PbmVvolTypeSwap = PbmVvolType("Swap") +) + +func init() { + types.Add("pbm:PbmVvolType", reflect.TypeOf((*PbmVvolType)(nil)).Elem()) +} diff --git a/vendor/github.com/vmware/govmomi/pbm/types/if.go b/vendor/github.com/vmware/govmomi/pbm/types/if.go new file mode 100644 index 00000000..2a3ac5ac --- /dev/null +++ b/vendor/github.com/vmware/govmomi/pbm/types/if.go @@ -0,0 +1,139 @@ +/* +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 types + +import ( + "reflect" + + "github.com/vmware/govmomi/vim25/types" +) + +func (b *PbmCapabilityConstraints) GetPbmCapabilityConstraints() *PbmCapabilityConstraints { return b } + +type BasePbmCapabilityConstraints interface { + GetPbmCapabilityConstraints() *PbmCapabilityConstraints +} + +func init() { + types.Add("BasePbmCapabilityConstraints", reflect.TypeOf((*PbmCapabilityConstraints)(nil)).Elem()) +} + +func (b *PbmCapabilityProfile) GetPbmCapabilityProfile() *PbmCapabilityProfile { return b } + +type BasePbmCapabilityProfile interface { + GetPbmCapabilityProfile() *PbmCapabilityProfile +} + +func init() { + types.Add("BasePbmCapabilityProfile", reflect.TypeOf((*PbmCapabilityProfile)(nil)).Elem()) +} + +func (b *PbmCapabilityProfilePropertyMismatchFault) GetPbmCapabilityProfilePropertyMismatchFault() *PbmCapabilityProfilePropertyMismatchFault { + return b +} + +type BasePbmCapabilityProfilePropertyMismatchFault interface { + GetPbmCapabilityProfilePropertyMismatchFault() *PbmCapabilityProfilePropertyMismatchFault +} + +func init() { + types.Add("BasePbmCapabilityProfilePropertyMismatchFault", reflect.TypeOf((*PbmCapabilityProfilePropertyMismatchFault)(nil)).Elem()) +} + +func (b *PbmCapabilityTypeInfo) GetPbmCapabilityTypeInfo() *PbmCapabilityTypeInfo { return b } + +type BasePbmCapabilityTypeInfo interface { + GetPbmCapabilityTypeInfo() *PbmCapabilityTypeInfo +} + +func init() { + types.Add("BasePbmCapabilityTypeInfo", reflect.TypeOf((*PbmCapabilityTypeInfo)(nil)).Elem()) +} + +func (b *PbmCompatibilityCheckFault) GetPbmCompatibilityCheckFault() *PbmCompatibilityCheckFault { + return b +} + +type BasePbmCompatibilityCheckFault interface { + GetPbmCompatibilityCheckFault() *PbmCompatibilityCheckFault +} + +func init() { + types.Add("BasePbmCompatibilityCheckFault", reflect.TypeOf((*PbmCompatibilityCheckFault)(nil)).Elem()) +} + +func (b *PbmFault) GetPbmFault() *PbmFault { return b } + +type BasePbmFault interface { + GetPbmFault() *PbmFault +} + +func init() { + types.Add("BasePbmFault", reflect.TypeOf((*PbmFault)(nil)).Elem()) +} + +func (b *PbmLineOfServiceInfo) GetPbmLineOfServiceInfo() *PbmLineOfServiceInfo { return b } + +type BasePbmLineOfServiceInfo interface { + GetPbmLineOfServiceInfo() *PbmLineOfServiceInfo +} + +func init() { + types.Add("BasePbmLineOfServiceInfo", reflect.TypeOf((*PbmLineOfServiceInfo)(nil)).Elem()) +} + +func (b *PbmPlacementMatchingResources) GetPbmPlacementMatchingResources() *PbmPlacementMatchingResources { + return b +} + +type BasePbmPlacementMatchingResources interface { + GetPbmPlacementMatchingResources() *PbmPlacementMatchingResources +} + +func init() { + types.Add("BasePbmPlacementMatchingResources", reflect.TypeOf((*PbmPlacementMatchingResources)(nil)).Elem()) +} + +func (b *PbmPlacementRequirement) GetPbmPlacementRequirement() *PbmPlacementRequirement { return b } + +type BasePbmPlacementRequirement interface { + GetPbmPlacementRequirement() *PbmPlacementRequirement +} + +func init() { + types.Add("BasePbmPlacementRequirement", reflect.TypeOf((*PbmPlacementRequirement)(nil)).Elem()) +} + +func (b *PbmProfile) GetPbmProfile() *PbmProfile { return b } + +type BasePbmProfile interface { + GetPbmProfile() *PbmProfile +} + +func init() { + types.Add("BasePbmProfile", reflect.TypeOf((*PbmProfile)(nil)).Elem()) +} + +func (b *PbmPropertyMismatchFault) GetPbmPropertyMismatchFault() *PbmPropertyMismatchFault { return b } + +type BasePbmPropertyMismatchFault interface { + GetPbmPropertyMismatchFault() *PbmPropertyMismatchFault +} + +func init() { + types.Add("BasePbmPropertyMismatchFault", reflect.TypeOf((*PbmPropertyMismatchFault)(nil)).Elem()) +} diff --git a/vendor/github.com/vmware/govmomi/pbm/types/types.go b/vendor/github.com/vmware/govmomi/pbm/types/types.go new file mode 100644 index 00000000..47bd73c1 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/pbm/types/types.go @@ -0,0 +1,1712 @@ +/* +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 types + +import ( + "reflect" + "time" + + "github.com/vmware/govmomi/vim25/types" +) + +type ArrayOfPbmCapabilityConstraintInstance struct { + PbmCapabilityConstraintInstance []PbmCapabilityConstraintInstance `xml:"PbmCapabilityConstraintInstance,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmCapabilityConstraintInstance", reflect.TypeOf((*ArrayOfPbmCapabilityConstraintInstance)(nil)).Elem()) +} + +type ArrayOfPbmCapabilityInstance struct { + PbmCapabilityInstance []PbmCapabilityInstance `xml:"PbmCapabilityInstance,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmCapabilityInstance", reflect.TypeOf((*ArrayOfPbmCapabilityInstance)(nil)).Elem()) +} + +type ArrayOfPbmCapabilityMetadata struct { + PbmCapabilityMetadata []PbmCapabilityMetadata `xml:"PbmCapabilityMetadata,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmCapabilityMetadata", reflect.TypeOf((*ArrayOfPbmCapabilityMetadata)(nil)).Elem()) +} + +type ArrayOfPbmCapabilityMetadataPerCategory struct { + PbmCapabilityMetadataPerCategory []PbmCapabilityMetadataPerCategory `xml:"PbmCapabilityMetadataPerCategory,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmCapabilityMetadataPerCategory", reflect.TypeOf((*ArrayOfPbmCapabilityMetadataPerCategory)(nil)).Elem()) +} + +type ArrayOfPbmCapabilityPropertyInstance struct { + PbmCapabilityPropertyInstance []PbmCapabilityPropertyInstance `xml:"PbmCapabilityPropertyInstance,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmCapabilityPropertyInstance", reflect.TypeOf((*ArrayOfPbmCapabilityPropertyInstance)(nil)).Elem()) +} + +type ArrayOfPbmCapabilityPropertyMetadata struct { + PbmCapabilityPropertyMetadata []PbmCapabilityPropertyMetadata `xml:"PbmCapabilityPropertyMetadata,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmCapabilityPropertyMetadata", reflect.TypeOf((*ArrayOfPbmCapabilityPropertyMetadata)(nil)).Elem()) +} + +type ArrayOfPbmCapabilitySchema struct { + PbmCapabilitySchema []PbmCapabilitySchema `xml:"PbmCapabilitySchema,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmCapabilitySchema", reflect.TypeOf((*ArrayOfPbmCapabilitySchema)(nil)).Elem()) +} + +type ArrayOfPbmCapabilitySubProfile struct { + PbmCapabilitySubProfile []PbmCapabilitySubProfile `xml:"PbmCapabilitySubProfile,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmCapabilitySubProfile", reflect.TypeOf((*ArrayOfPbmCapabilitySubProfile)(nil)).Elem()) +} + +type ArrayOfPbmCapabilityVendorNamespaceInfo struct { + PbmCapabilityVendorNamespaceInfo []PbmCapabilityVendorNamespaceInfo `xml:"PbmCapabilityVendorNamespaceInfo,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmCapabilityVendorNamespaceInfo", reflect.TypeOf((*ArrayOfPbmCapabilityVendorNamespaceInfo)(nil)).Elem()) +} + +type ArrayOfPbmCapabilityVendorResourceTypeInfo struct { + PbmCapabilityVendorResourceTypeInfo []PbmCapabilityVendorResourceTypeInfo `xml:"PbmCapabilityVendorResourceTypeInfo,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmCapabilityVendorResourceTypeInfo", reflect.TypeOf((*ArrayOfPbmCapabilityVendorResourceTypeInfo)(nil)).Elem()) +} + +type ArrayOfPbmCompliancePolicyStatus struct { + PbmCompliancePolicyStatus []PbmCompliancePolicyStatus `xml:"PbmCompliancePolicyStatus,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmCompliancePolicyStatus", reflect.TypeOf((*ArrayOfPbmCompliancePolicyStatus)(nil)).Elem()) +} + +type ArrayOfPbmComplianceResult struct { + PbmComplianceResult []PbmComplianceResult `xml:"PbmComplianceResult,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmComplianceResult", reflect.TypeOf((*ArrayOfPbmComplianceResult)(nil)).Elem()) +} + +type ArrayOfPbmDatastoreSpaceStatistics struct { + PbmDatastoreSpaceStatistics []PbmDatastoreSpaceStatistics `xml:"PbmDatastoreSpaceStatistics,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmDatastoreSpaceStatistics", reflect.TypeOf((*ArrayOfPbmDatastoreSpaceStatistics)(nil)).Elem()) +} + +type ArrayOfPbmDefaultProfileInfo struct { + PbmDefaultProfileInfo []PbmDefaultProfileInfo `xml:"PbmDefaultProfileInfo,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmDefaultProfileInfo", reflect.TypeOf((*ArrayOfPbmDefaultProfileInfo)(nil)).Elem()) +} + +type ArrayOfPbmPlacementCompatibilityResult struct { + PbmPlacementCompatibilityResult []PbmPlacementCompatibilityResult `xml:"PbmPlacementCompatibilityResult,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmPlacementCompatibilityResult", reflect.TypeOf((*ArrayOfPbmPlacementCompatibilityResult)(nil)).Elem()) +} + +type ArrayOfPbmPlacementHub struct { + PbmPlacementHub []PbmPlacementHub `xml:"PbmPlacementHub,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmPlacementHub", reflect.TypeOf((*ArrayOfPbmPlacementHub)(nil)).Elem()) +} + +type ArrayOfPbmPlacementMatchingResources struct { + PbmPlacementMatchingResources []BasePbmPlacementMatchingResources `xml:"PbmPlacementMatchingResources,omitempty,typeattr"` +} + +func init() { + types.Add("pbm:ArrayOfPbmPlacementMatchingResources", reflect.TypeOf((*ArrayOfPbmPlacementMatchingResources)(nil)).Elem()) +} + +type ArrayOfPbmPlacementRequirement struct { + PbmPlacementRequirement []BasePbmPlacementRequirement `xml:"PbmPlacementRequirement,omitempty,typeattr"` +} + +func init() { + types.Add("pbm:ArrayOfPbmPlacementRequirement", reflect.TypeOf((*ArrayOfPbmPlacementRequirement)(nil)).Elem()) +} + +type ArrayOfPbmPlacementResourceUtilization struct { + PbmPlacementResourceUtilization []PbmPlacementResourceUtilization `xml:"PbmPlacementResourceUtilization,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmPlacementResourceUtilization", reflect.TypeOf((*ArrayOfPbmPlacementResourceUtilization)(nil)).Elem()) +} + +type ArrayOfPbmProfile struct { + PbmProfile []BasePbmProfile `xml:"PbmProfile,omitempty,typeattr"` +} + +func init() { + types.Add("pbm:ArrayOfPbmProfile", reflect.TypeOf((*ArrayOfPbmProfile)(nil)).Elem()) +} + +type ArrayOfPbmProfileId struct { + PbmProfileId []PbmProfileId `xml:"PbmProfileId,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmProfileId", reflect.TypeOf((*ArrayOfPbmProfileId)(nil)).Elem()) +} + +type ArrayOfPbmProfileOperationOutcome struct { + PbmProfileOperationOutcome []PbmProfileOperationOutcome `xml:"PbmProfileOperationOutcome,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmProfileOperationOutcome", reflect.TypeOf((*ArrayOfPbmProfileOperationOutcome)(nil)).Elem()) +} + +type ArrayOfPbmProfileResourceType struct { + PbmProfileResourceType []PbmProfileResourceType `xml:"PbmProfileResourceType,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmProfileResourceType", reflect.TypeOf((*ArrayOfPbmProfileResourceType)(nil)).Elem()) +} + +type ArrayOfPbmProfileType struct { + PbmProfileType []PbmProfileType `xml:"PbmProfileType,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmProfileType", reflect.TypeOf((*ArrayOfPbmProfileType)(nil)).Elem()) +} + +type ArrayOfPbmQueryProfileResult struct { + PbmQueryProfileResult []PbmQueryProfileResult `xml:"PbmQueryProfileResult,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmQueryProfileResult", reflect.TypeOf((*ArrayOfPbmQueryProfileResult)(nil)).Elem()) +} + +type ArrayOfPbmQueryReplicationGroupResult struct { + PbmQueryReplicationGroupResult []PbmQueryReplicationGroupResult `xml:"PbmQueryReplicationGroupResult,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmQueryReplicationGroupResult", reflect.TypeOf((*ArrayOfPbmQueryReplicationGroupResult)(nil)).Elem()) +} + +type ArrayOfPbmRollupComplianceResult struct { + PbmRollupComplianceResult []PbmRollupComplianceResult `xml:"PbmRollupComplianceResult,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmRollupComplianceResult", reflect.TypeOf((*ArrayOfPbmRollupComplianceResult)(nil)).Elem()) +} + +type ArrayOfPbmServerObjectRef struct { + PbmServerObjectRef []PbmServerObjectRef `xml:"PbmServerObjectRef,omitempty"` +} + +func init() { + types.Add("pbm:ArrayOfPbmServerObjectRef", reflect.TypeOf((*ArrayOfPbmServerObjectRef)(nil)).Elem()) +} + +type PbmAboutInfo struct { + types.DynamicData + + Name string `xml:"name"` + Version string `xml:"version"` + InstanceUuid string `xml:"instanceUuid"` +} + +func init() { + types.Add("pbm:PbmAboutInfo", reflect.TypeOf((*PbmAboutInfo)(nil)).Elem()) +} + +type PbmAlreadyExists struct { + PbmFault + + Name string `xml:"name,omitempty"` +} + +func init() { + types.Add("pbm:PbmAlreadyExists", reflect.TypeOf((*PbmAlreadyExists)(nil)).Elem()) +} + +type PbmAssignDefaultRequirementProfile PbmAssignDefaultRequirementProfileRequestType + +func init() { + types.Add("pbm:PbmAssignDefaultRequirementProfile", reflect.TypeOf((*PbmAssignDefaultRequirementProfile)(nil)).Elem()) +} + +type PbmAssignDefaultRequirementProfileRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Profile PbmProfileId `xml:"profile"` + Datastores []PbmPlacementHub `xml:"datastores"` +} + +func init() { + types.Add("pbm:PbmAssignDefaultRequirementProfileRequestType", reflect.TypeOf((*PbmAssignDefaultRequirementProfileRequestType)(nil)).Elem()) +} + +type PbmAssignDefaultRequirementProfileResponse struct { +} + +type PbmCapabilityConstraintInstance struct { + types.DynamicData + + PropertyInstance []PbmCapabilityPropertyInstance `xml:"propertyInstance"` +} + +func init() { + types.Add("pbm:PbmCapabilityConstraintInstance", reflect.TypeOf((*PbmCapabilityConstraintInstance)(nil)).Elem()) +} + +type PbmCapabilityConstraints struct { + types.DynamicData +} + +func init() { + types.Add("pbm:PbmCapabilityConstraints", reflect.TypeOf((*PbmCapabilityConstraints)(nil)).Elem()) +} + +type PbmCapabilityDescription struct { + types.DynamicData + + Description PbmExtendedElementDescription `xml:"description"` + Value types.AnyType `xml:"value,typeattr"` +} + +func init() { + types.Add("pbm:PbmCapabilityDescription", reflect.TypeOf((*PbmCapabilityDescription)(nil)).Elem()) +} + +type PbmCapabilityDiscreteSet struct { + types.DynamicData + + Values []types.AnyType `xml:"values,typeattr"` +} + +func init() { + types.Add("pbm:PbmCapabilityDiscreteSet", reflect.TypeOf((*PbmCapabilityDiscreteSet)(nil)).Elem()) +} + +type PbmCapabilityGenericTypeInfo struct { + PbmCapabilityTypeInfo + + GenericTypeName string `xml:"genericTypeName"` +} + +func init() { + types.Add("pbm:PbmCapabilityGenericTypeInfo", reflect.TypeOf((*PbmCapabilityGenericTypeInfo)(nil)).Elem()) +} + +type PbmCapabilityInstance struct { + types.DynamicData + + Id PbmCapabilityMetadataUniqueId `xml:"id"` + Constraint []PbmCapabilityConstraintInstance `xml:"constraint"` +} + +func init() { + types.Add("pbm:PbmCapabilityInstance", reflect.TypeOf((*PbmCapabilityInstance)(nil)).Elem()) +} + +type PbmCapabilityMetadata struct { + types.DynamicData + + Id PbmCapabilityMetadataUniqueId `xml:"id"` + Summary PbmExtendedElementDescription `xml:"summary"` + Mandatory *bool `xml:"mandatory"` + Hint *bool `xml:"hint"` + KeyId string `xml:"keyId,omitempty"` + AllowMultipleConstraints *bool `xml:"allowMultipleConstraints"` + PropertyMetadata []PbmCapabilityPropertyMetadata `xml:"propertyMetadata"` +} + +func init() { + types.Add("pbm:PbmCapabilityMetadata", reflect.TypeOf((*PbmCapabilityMetadata)(nil)).Elem()) +} + +type PbmCapabilityMetadataPerCategory struct { + types.DynamicData + + SubCategory string `xml:"subCategory"` + CapabilityMetadata []PbmCapabilityMetadata `xml:"capabilityMetadata"` +} + +func init() { + types.Add("pbm:PbmCapabilityMetadataPerCategory", reflect.TypeOf((*PbmCapabilityMetadataPerCategory)(nil)).Elem()) +} + +type PbmCapabilityMetadataUniqueId struct { + types.DynamicData + + Namespace string `xml:"namespace"` + Id string `xml:"id"` +} + +func init() { + types.Add("pbm:PbmCapabilityMetadataUniqueId", reflect.TypeOf((*PbmCapabilityMetadataUniqueId)(nil)).Elem()) +} + +type PbmCapabilityNamespaceInfo struct { + types.DynamicData + + Version string `xml:"version"` + Namespace string `xml:"namespace"` + Info *PbmExtendedElementDescription `xml:"info,omitempty"` +} + +func init() { + types.Add("pbm:PbmCapabilityNamespaceInfo", reflect.TypeOf((*PbmCapabilityNamespaceInfo)(nil)).Elem()) +} + +type PbmCapabilityProfile struct { + PbmProfile + + ProfileCategory string `xml:"profileCategory"` + ResourceType PbmProfileResourceType `xml:"resourceType"` + Constraints BasePbmCapabilityConstraints `xml:"constraints,typeattr"` + GenerationId int64 `xml:"generationId,omitempty"` + IsDefault bool `xml:"isDefault"` + SystemCreatedProfileType string `xml:"systemCreatedProfileType,omitempty"` + LineOfService string `xml:"lineOfService,omitempty"` +} + +func init() { + types.Add("pbm:PbmCapabilityProfile", reflect.TypeOf((*PbmCapabilityProfile)(nil)).Elem()) +} + +type PbmCapabilityProfileCreateSpec struct { + types.DynamicData + + Name string `xml:"name"` + Description string `xml:"description,omitempty"` + Category string `xml:"category,omitempty"` + ResourceType PbmProfileResourceType `xml:"resourceType"` + Constraints BasePbmCapabilityConstraints `xml:"constraints,typeattr"` +} + +func init() { + types.Add("pbm:PbmCapabilityProfileCreateSpec", reflect.TypeOf((*PbmCapabilityProfileCreateSpec)(nil)).Elem()) +} + +type PbmCapabilityProfilePropertyMismatchFault struct { + PbmPropertyMismatchFault + + ResourcePropertyInstance PbmCapabilityPropertyInstance `xml:"resourcePropertyInstance"` +} + +func init() { + types.Add("pbm:PbmCapabilityProfilePropertyMismatchFault", reflect.TypeOf((*PbmCapabilityProfilePropertyMismatchFault)(nil)).Elem()) +} + +type PbmCapabilityProfilePropertyMismatchFaultFault BasePbmCapabilityProfilePropertyMismatchFault + +func init() { + types.Add("pbm:PbmCapabilityProfilePropertyMismatchFaultFault", reflect.TypeOf((*PbmCapabilityProfilePropertyMismatchFaultFault)(nil)).Elem()) +} + +type PbmCapabilityProfileUpdateSpec struct { + types.DynamicData + + Name string `xml:"name,omitempty"` + Description string `xml:"description,omitempty"` + Constraints BasePbmCapabilityConstraints `xml:"constraints,omitempty,typeattr"` +} + +func init() { + types.Add("pbm:PbmCapabilityProfileUpdateSpec", reflect.TypeOf((*PbmCapabilityProfileUpdateSpec)(nil)).Elem()) +} + +type PbmCapabilityPropertyInstance struct { + types.DynamicData + + Id string `xml:"id"` + Operator string `xml:"operator,omitempty"` + Value types.AnyType `xml:"value,typeattr"` +} + +func init() { + types.Add("pbm:PbmCapabilityPropertyInstance", reflect.TypeOf((*PbmCapabilityPropertyInstance)(nil)).Elem()) +} + +type PbmCapabilityPropertyMetadata struct { + types.DynamicData + + Id string `xml:"id"` + Summary PbmExtendedElementDescription `xml:"summary"` + Mandatory bool `xml:"mandatory"` + Type BasePbmCapabilityTypeInfo `xml:"type,omitempty,typeattr"` + DefaultValue types.AnyType `xml:"defaultValue,omitempty,typeattr"` + AllowedValue types.AnyType `xml:"allowedValue,omitempty,typeattr"` + RequirementsTypeHint string `xml:"requirementsTypeHint,omitempty"` +} + +func init() { + types.Add("pbm:PbmCapabilityPropertyMetadata", reflect.TypeOf((*PbmCapabilityPropertyMetadata)(nil)).Elem()) +} + +type PbmCapabilityRange struct { + types.DynamicData + + Min types.AnyType `xml:"min,typeattr"` + Max types.AnyType `xml:"max,typeattr"` +} + +func init() { + types.Add("pbm:PbmCapabilityRange", reflect.TypeOf((*PbmCapabilityRange)(nil)).Elem()) +} + +type PbmCapabilitySchema struct { + types.DynamicData + + VendorInfo PbmCapabilitySchemaVendorInfo `xml:"vendorInfo"` + NamespaceInfo PbmCapabilityNamespaceInfo `xml:"namespaceInfo"` + LineOfService BasePbmLineOfServiceInfo `xml:"lineOfService,omitempty,typeattr"` + CapabilityMetadataPerCategory []PbmCapabilityMetadataPerCategory `xml:"capabilityMetadataPerCategory"` +} + +func init() { + types.Add("pbm:PbmCapabilitySchema", reflect.TypeOf((*PbmCapabilitySchema)(nil)).Elem()) +} + +type PbmCapabilitySchemaVendorInfo struct { + types.DynamicData + + VendorUuid string `xml:"vendorUuid"` + Info PbmExtendedElementDescription `xml:"info"` +} + +func init() { + types.Add("pbm:PbmCapabilitySchemaVendorInfo", reflect.TypeOf((*PbmCapabilitySchemaVendorInfo)(nil)).Elem()) +} + +type PbmCapabilitySubProfile struct { + types.DynamicData + + Name string `xml:"name"` + Capability []PbmCapabilityInstance `xml:"capability"` + ForceProvision *bool `xml:"forceProvision"` +} + +func init() { + types.Add("pbm:PbmCapabilitySubProfile", reflect.TypeOf((*PbmCapabilitySubProfile)(nil)).Elem()) +} + +type PbmCapabilitySubProfileConstraints struct { + PbmCapabilityConstraints + + SubProfiles []PbmCapabilitySubProfile `xml:"subProfiles"` +} + +func init() { + types.Add("pbm:PbmCapabilitySubProfileConstraints", reflect.TypeOf((*PbmCapabilitySubProfileConstraints)(nil)).Elem()) +} + +type PbmCapabilityTimeSpan struct { + types.DynamicData + + Value int32 `xml:"value"` + Unit string `xml:"unit"` +} + +func init() { + types.Add("pbm:PbmCapabilityTimeSpan", reflect.TypeOf((*PbmCapabilityTimeSpan)(nil)).Elem()) +} + +type PbmCapabilityTypeInfo struct { + types.DynamicData + + TypeName string `xml:"typeName"` +} + +func init() { + types.Add("pbm:PbmCapabilityTypeInfo", reflect.TypeOf((*PbmCapabilityTypeInfo)(nil)).Elem()) +} + +type PbmCapabilityVendorNamespaceInfo struct { + types.DynamicData + + VendorInfo PbmCapabilitySchemaVendorInfo `xml:"vendorInfo"` + NamespaceInfo PbmCapabilityNamespaceInfo `xml:"namespaceInfo"` +} + +func init() { + types.Add("pbm:PbmCapabilityVendorNamespaceInfo", reflect.TypeOf((*PbmCapabilityVendorNamespaceInfo)(nil)).Elem()) +} + +type PbmCapabilityVendorResourceTypeInfo struct { + types.DynamicData + + ResourceType string `xml:"resourceType"` + VendorNamespaceInfo []PbmCapabilityVendorNamespaceInfo `xml:"vendorNamespaceInfo"` +} + +func init() { + types.Add("pbm:PbmCapabilityVendorResourceTypeInfo", reflect.TypeOf((*PbmCapabilityVendorResourceTypeInfo)(nil)).Elem()) +} + +type PbmCheckCompatibility PbmCheckCompatibilityRequestType + +func init() { + types.Add("pbm:PbmCheckCompatibility", reflect.TypeOf((*PbmCheckCompatibility)(nil)).Elem()) +} + +type PbmCheckCompatibilityRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + HubsToSearch []PbmPlacementHub `xml:"hubsToSearch,omitempty"` + Profile PbmProfileId `xml:"profile"` +} + +func init() { + types.Add("pbm:PbmCheckCompatibilityRequestType", reflect.TypeOf((*PbmCheckCompatibilityRequestType)(nil)).Elem()) +} + +type PbmCheckCompatibilityResponse struct { + Returnval []PbmPlacementCompatibilityResult `xml:"returnval,omitempty"` +} + +type PbmCheckCompatibilityWithSpec PbmCheckCompatibilityWithSpecRequestType + +func init() { + types.Add("pbm:PbmCheckCompatibilityWithSpec", reflect.TypeOf((*PbmCheckCompatibilityWithSpec)(nil)).Elem()) +} + +type PbmCheckCompatibilityWithSpecRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + HubsToSearch []PbmPlacementHub `xml:"hubsToSearch,omitempty"` + ProfileSpec PbmCapabilityProfileCreateSpec `xml:"profileSpec"` +} + +func init() { + types.Add("pbm:PbmCheckCompatibilityWithSpecRequestType", reflect.TypeOf((*PbmCheckCompatibilityWithSpecRequestType)(nil)).Elem()) +} + +type PbmCheckCompatibilityWithSpecResponse struct { + Returnval []PbmPlacementCompatibilityResult `xml:"returnval,omitempty"` +} + +type PbmCheckCompliance PbmCheckComplianceRequestType + +func init() { + types.Add("pbm:PbmCheckCompliance", reflect.TypeOf((*PbmCheckCompliance)(nil)).Elem()) +} + +type PbmCheckComplianceRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Entities []PbmServerObjectRef `xml:"entities"` + Profile *PbmProfileId `xml:"profile,omitempty"` +} + +func init() { + types.Add("pbm:PbmCheckComplianceRequestType", reflect.TypeOf((*PbmCheckComplianceRequestType)(nil)).Elem()) +} + +type PbmCheckComplianceResponse struct { + Returnval []PbmComplianceResult `xml:"returnval,omitempty"` +} + +type PbmCheckRequirements PbmCheckRequirementsRequestType + +func init() { + types.Add("pbm:PbmCheckRequirements", reflect.TypeOf((*PbmCheckRequirements)(nil)).Elem()) +} + +type PbmCheckRequirementsRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + HubsToSearch []PbmPlacementHub `xml:"hubsToSearch,omitempty"` + PlacementSubjectRef *PbmServerObjectRef `xml:"placementSubjectRef,omitempty"` + PlacementSubjectRequirement []BasePbmPlacementRequirement `xml:"placementSubjectRequirement,omitempty,typeattr"` +} + +func init() { + types.Add("pbm:PbmCheckRequirementsRequestType", reflect.TypeOf((*PbmCheckRequirementsRequestType)(nil)).Elem()) +} + +type PbmCheckRequirementsResponse struct { + Returnval []PbmPlacementCompatibilityResult `xml:"returnval,omitempty"` +} + +type PbmCheckRollupCompliance PbmCheckRollupComplianceRequestType + +func init() { + types.Add("pbm:PbmCheckRollupCompliance", reflect.TypeOf((*PbmCheckRollupCompliance)(nil)).Elem()) +} + +type PbmCheckRollupComplianceRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Entity []PbmServerObjectRef `xml:"entity"` +} + +func init() { + types.Add("pbm:PbmCheckRollupComplianceRequestType", reflect.TypeOf((*PbmCheckRollupComplianceRequestType)(nil)).Elem()) +} + +type PbmCheckRollupComplianceResponse struct { + Returnval []PbmRollupComplianceResult `xml:"returnval,omitempty"` +} + +type PbmCompatibilityCheckFault struct { + PbmFault + + Hub PbmPlacementHub `xml:"hub"` +} + +func init() { + types.Add("pbm:PbmCompatibilityCheckFault", reflect.TypeOf((*PbmCompatibilityCheckFault)(nil)).Elem()) +} + +type PbmCompatibilityCheckFaultFault BasePbmCompatibilityCheckFault + +func init() { + types.Add("pbm:PbmCompatibilityCheckFaultFault", reflect.TypeOf((*PbmCompatibilityCheckFaultFault)(nil)).Elem()) +} + +type PbmComplianceOperationalStatus struct { + types.DynamicData + + Healthy *bool `xml:"healthy"` + OperationETA *time.Time `xml:"operationETA"` + OperationProgress int64 `xml:"operationProgress,omitempty"` + Transitional *bool `xml:"transitional"` +} + +func init() { + types.Add("pbm:PbmComplianceOperationalStatus", reflect.TypeOf((*PbmComplianceOperationalStatus)(nil)).Elem()) +} + +type PbmCompliancePolicyStatus struct { + types.DynamicData + + ExpectedValue PbmCapabilityInstance `xml:"expectedValue"` + CurrentValue *PbmCapabilityInstance `xml:"currentValue,omitempty"` +} + +func init() { + types.Add("pbm:PbmCompliancePolicyStatus", reflect.TypeOf((*PbmCompliancePolicyStatus)(nil)).Elem()) +} + +type PbmComplianceResult struct { + types.DynamicData + + CheckTime time.Time `xml:"checkTime"` + Entity PbmServerObjectRef `xml:"entity"` + Profile *PbmProfileId `xml:"profile,omitempty"` + ComplianceTaskStatus string `xml:"complianceTaskStatus,omitempty"` + ComplianceStatus string `xml:"complianceStatus"` + Mismatch bool `xml:"mismatch"` + ViolatedPolicies []PbmCompliancePolicyStatus `xml:"violatedPolicies,omitempty"` + ErrorCause []types.LocalizedMethodFault `xml:"errorCause,omitempty"` + OperationalStatus *PbmComplianceOperationalStatus `xml:"operationalStatus,omitempty"` + Info *PbmExtendedElementDescription `xml:"info,omitempty"` +} + +func init() { + types.Add("pbm:PbmComplianceResult", reflect.TypeOf((*PbmComplianceResult)(nil)).Elem()) +} + +type PbmCreate PbmCreateRequestType + +func init() { + types.Add("pbm:PbmCreate", reflect.TypeOf((*PbmCreate)(nil)).Elem()) +} + +type PbmCreateRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + CreateSpec PbmCapabilityProfileCreateSpec `xml:"createSpec"` +} + +func init() { + types.Add("pbm:PbmCreateRequestType", reflect.TypeOf((*PbmCreateRequestType)(nil)).Elem()) +} + +type PbmCreateResponse struct { + Returnval PbmProfileId `xml:"returnval"` +} + +type PbmDataServiceToPoliciesMap struct { + types.DynamicData + + DataServicePolicy PbmProfileId `xml:"dataServicePolicy"` + ParentStoragePolicies []PbmProfileId `xml:"parentStoragePolicies,omitempty"` + Fault *types.LocalizedMethodFault `xml:"fault,omitempty"` +} + +func init() { + types.Add("pbm:PbmDataServiceToPoliciesMap", reflect.TypeOf((*PbmDataServiceToPoliciesMap)(nil)).Elem()) +} + +type PbmDatastoreSpaceStatistics struct { + types.DynamicData + + ProfileId string `xml:"profileId,omitempty"` + PhysicalTotalInMB int64 `xml:"physicalTotalInMB"` + PhysicalFreeInMB int64 `xml:"physicalFreeInMB"` + PhysicalUsedInMB int64 `xml:"physicalUsedInMB"` + LogicalLimitInMB int64 `xml:"logicalLimitInMB,omitempty"` + LogicalFreeInMB int64 `xml:"logicalFreeInMB"` + LogicalUsedInMB int64 `xml:"logicalUsedInMB"` +} + +func init() { + types.Add("pbm:PbmDatastoreSpaceStatistics", reflect.TypeOf((*PbmDatastoreSpaceStatistics)(nil)).Elem()) +} + +type PbmDefaultCapabilityProfile struct { + PbmCapabilityProfile + + VvolType []string `xml:"vvolType"` + ContainerId string `xml:"containerId"` +} + +func init() { + types.Add("pbm:PbmDefaultCapabilityProfile", reflect.TypeOf((*PbmDefaultCapabilityProfile)(nil)).Elem()) +} + +type PbmDefaultProfileAppliesFault struct { + PbmCompatibilityCheckFault +} + +func init() { + types.Add("pbm:PbmDefaultProfileAppliesFault", reflect.TypeOf((*PbmDefaultProfileAppliesFault)(nil)).Elem()) +} + +type PbmDefaultProfileAppliesFaultFault PbmDefaultProfileAppliesFault + +func init() { + types.Add("pbm:PbmDefaultProfileAppliesFaultFault", reflect.TypeOf((*PbmDefaultProfileAppliesFaultFault)(nil)).Elem()) +} + +type PbmDefaultProfileInfo struct { + types.DynamicData + + Datastores []PbmPlacementHub `xml:"datastores"` + DefaultProfile BasePbmProfile `xml:"defaultProfile,omitempty,typeattr"` +} + +func init() { + types.Add("pbm:PbmDefaultProfileInfo", reflect.TypeOf((*PbmDefaultProfileInfo)(nil)).Elem()) +} + +type PbmDelete PbmDeleteRequestType + +func init() { + types.Add("pbm:PbmDelete", reflect.TypeOf((*PbmDelete)(nil)).Elem()) +} + +type PbmDeleteRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + ProfileId []PbmProfileId `xml:"profileId"` +} + +func init() { + types.Add("pbm:PbmDeleteRequestType", reflect.TypeOf((*PbmDeleteRequestType)(nil)).Elem()) +} + +type PbmDeleteResponse struct { + Returnval []PbmProfileOperationOutcome `xml:"returnval,omitempty"` +} + +type PbmDuplicateName struct { + PbmFault + + Name string `xml:"name"` +} + +func init() { + types.Add("pbm:PbmDuplicateName", reflect.TypeOf((*PbmDuplicateName)(nil)).Elem()) +} + +type PbmDuplicateNameFault PbmDuplicateName + +func init() { + types.Add("pbm:PbmDuplicateNameFault", reflect.TypeOf((*PbmDuplicateNameFault)(nil)).Elem()) +} + +type PbmExtendedElementDescription struct { + types.DynamicData + + Label string `xml:"label"` + Summary string `xml:"summary"` + Key string `xml:"key"` + MessageCatalogKeyPrefix string `xml:"messageCatalogKeyPrefix"` + MessageArg []types.KeyAnyValue `xml:"messageArg,omitempty"` +} + +func init() { + types.Add("pbm:PbmExtendedElementDescription", reflect.TypeOf((*PbmExtendedElementDescription)(nil)).Elem()) +} + +type PbmFault struct { + types.MethodFault +} + +func init() { + types.Add("pbm:PbmFault", reflect.TypeOf((*PbmFault)(nil)).Elem()) +} + +type PbmFaultFault BasePbmFault + +func init() { + types.Add("pbm:PbmFaultFault", reflect.TypeOf((*PbmFaultFault)(nil)).Elem()) +} + +type PbmFaultInvalidLogin struct { + PbmFault +} + +func init() { + types.Add("pbm:PbmFaultInvalidLogin", reflect.TypeOf((*PbmFaultInvalidLogin)(nil)).Elem()) +} + +type PbmFaultInvalidLoginFault PbmFaultInvalidLogin + +func init() { + types.Add("pbm:PbmFaultInvalidLoginFault", reflect.TypeOf((*PbmFaultInvalidLoginFault)(nil)).Elem()) +} + +type PbmFaultNotFound struct { + PbmFault +} + +func init() { + types.Add("pbm:PbmFaultNotFound", reflect.TypeOf((*PbmFaultNotFound)(nil)).Elem()) +} + +type PbmFaultNotFoundFault PbmFaultNotFound + +func init() { + types.Add("pbm:PbmFaultNotFoundFault", reflect.TypeOf((*PbmFaultNotFoundFault)(nil)).Elem()) +} + +type PbmFaultProfileStorageFault struct { + PbmFault +} + +func init() { + types.Add("pbm:PbmFaultProfileStorageFault", reflect.TypeOf((*PbmFaultProfileStorageFault)(nil)).Elem()) +} + +type PbmFaultProfileStorageFaultFault PbmFaultProfileStorageFault + +func init() { + types.Add("pbm:PbmFaultProfileStorageFaultFault", reflect.TypeOf((*PbmFaultProfileStorageFaultFault)(nil)).Elem()) +} + +type PbmFetchCapabilityMetadata PbmFetchCapabilityMetadataRequestType + +func init() { + types.Add("pbm:PbmFetchCapabilityMetadata", reflect.TypeOf((*PbmFetchCapabilityMetadata)(nil)).Elem()) +} + +type PbmFetchCapabilityMetadataRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + ResourceType *PbmProfileResourceType `xml:"resourceType,omitempty"` + VendorUuid string `xml:"vendorUuid,omitempty"` +} + +func init() { + types.Add("pbm:PbmFetchCapabilityMetadataRequestType", reflect.TypeOf((*PbmFetchCapabilityMetadataRequestType)(nil)).Elem()) +} + +type PbmFetchCapabilityMetadataResponse struct { + Returnval []PbmCapabilityMetadataPerCategory `xml:"returnval,omitempty"` +} + +type PbmFetchCapabilitySchema PbmFetchCapabilitySchemaRequestType + +func init() { + types.Add("pbm:PbmFetchCapabilitySchema", reflect.TypeOf((*PbmFetchCapabilitySchema)(nil)).Elem()) +} + +type PbmFetchCapabilitySchemaRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + VendorUuid string `xml:"vendorUuid,omitempty"` + LineOfService []string `xml:"lineOfService,omitempty"` +} + +func init() { + types.Add("pbm:PbmFetchCapabilitySchemaRequestType", reflect.TypeOf((*PbmFetchCapabilitySchemaRequestType)(nil)).Elem()) +} + +type PbmFetchCapabilitySchemaResponse struct { + Returnval []PbmCapabilitySchema `xml:"returnval,omitempty"` +} + +type PbmFetchComplianceResult PbmFetchComplianceResultRequestType + +func init() { + types.Add("pbm:PbmFetchComplianceResult", reflect.TypeOf((*PbmFetchComplianceResult)(nil)).Elem()) +} + +type PbmFetchComplianceResultRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Entities []PbmServerObjectRef `xml:"entities"` + Profile *PbmProfileId `xml:"profile,omitempty"` +} + +func init() { + types.Add("pbm:PbmFetchComplianceResultRequestType", reflect.TypeOf((*PbmFetchComplianceResultRequestType)(nil)).Elem()) +} + +type PbmFetchComplianceResultResponse struct { + Returnval []PbmComplianceResult `xml:"returnval,omitempty"` +} + +type PbmFetchResourceType PbmFetchResourceTypeRequestType + +func init() { + types.Add("pbm:PbmFetchResourceType", reflect.TypeOf((*PbmFetchResourceType)(nil)).Elem()) +} + +type PbmFetchResourceTypeRequestType struct { + This types.ManagedObjectReference `xml:"_this"` +} + +func init() { + types.Add("pbm:PbmFetchResourceTypeRequestType", reflect.TypeOf((*PbmFetchResourceTypeRequestType)(nil)).Elem()) +} + +type PbmFetchResourceTypeResponse struct { + Returnval []PbmProfileResourceType `xml:"returnval,omitempty"` +} + +type PbmFetchRollupComplianceResult PbmFetchRollupComplianceResultRequestType + +func init() { + types.Add("pbm:PbmFetchRollupComplianceResult", reflect.TypeOf((*PbmFetchRollupComplianceResult)(nil)).Elem()) +} + +type PbmFetchRollupComplianceResultRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Entity []PbmServerObjectRef `xml:"entity"` +} + +func init() { + types.Add("pbm:PbmFetchRollupComplianceResultRequestType", reflect.TypeOf((*PbmFetchRollupComplianceResultRequestType)(nil)).Elem()) +} + +type PbmFetchRollupComplianceResultResponse struct { + Returnval []PbmRollupComplianceResult `xml:"returnval,omitempty"` +} + +type PbmFetchVendorInfo PbmFetchVendorInfoRequestType + +func init() { + types.Add("pbm:PbmFetchVendorInfo", reflect.TypeOf((*PbmFetchVendorInfo)(nil)).Elem()) +} + +type PbmFetchVendorInfoRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + ResourceType *PbmProfileResourceType `xml:"resourceType,omitempty"` +} + +func init() { + types.Add("pbm:PbmFetchVendorInfoRequestType", reflect.TypeOf((*PbmFetchVendorInfoRequestType)(nil)).Elem()) +} + +type PbmFetchVendorInfoResponse struct { + Returnval []PbmCapabilityVendorResourceTypeInfo `xml:"returnval,omitempty"` +} + +type PbmFindApplicableDefaultProfile PbmFindApplicableDefaultProfileRequestType + +func init() { + types.Add("pbm:PbmFindApplicableDefaultProfile", reflect.TypeOf((*PbmFindApplicableDefaultProfile)(nil)).Elem()) +} + +type PbmFindApplicableDefaultProfileRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Datastores []PbmPlacementHub `xml:"datastores"` +} + +func init() { + types.Add("pbm:PbmFindApplicableDefaultProfileRequestType", reflect.TypeOf((*PbmFindApplicableDefaultProfileRequestType)(nil)).Elem()) +} + +type PbmFindApplicableDefaultProfileResponse struct { + Returnval []BasePbmProfile `xml:"returnval,omitempty,typeattr"` +} + +type PbmIncompatibleVendorSpecificRuleSet struct { + PbmCapabilityProfilePropertyMismatchFault +} + +func init() { + types.Add("pbm:PbmIncompatibleVendorSpecificRuleSet", reflect.TypeOf((*PbmIncompatibleVendorSpecificRuleSet)(nil)).Elem()) +} + +type PbmIncompatibleVendorSpecificRuleSetFault PbmIncompatibleVendorSpecificRuleSet + +func init() { + types.Add("pbm:PbmIncompatibleVendorSpecificRuleSetFault", reflect.TypeOf((*PbmIncompatibleVendorSpecificRuleSetFault)(nil)).Elem()) +} + +type PbmLegacyHubsNotSupported struct { + PbmFault + + Hubs []PbmPlacementHub `xml:"hubs"` +} + +func init() { + types.Add("pbm:PbmLegacyHubsNotSupported", reflect.TypeOf((*PbmLegacyHubsNotSupported)(nil)).Elem()) +} + +type PbmLegacyHubsNotSupportedFault PbmLegacyHubsNotSupported + +func init() { + types.Add("pbm:PbmLegacyHubsNotSupportedFault", reflect.TypeOf((*PbmLegacyHubsNotSupportedFault)(nil)).Elem()) +} + +type PbmLineOfServiceInfo struct { + types.DynamicData + + LineOfService string `xml:"lineOfService"` + Name PbmExtendedElementDescription `xml:"name"` + Description *PbmExtendedElementDescription `xml:"description,omitempty"` +} + +func init() { + types.Add("pbm:PbmLineOfServiceInfo", reflect.TypeOf((*PbmLineOfServiceInfo)(nil)).Elem()) +} + +type PbmNonExistentHubs struct { + PbmFault + + Hubs []PbmPlacementHub `xml:"hubs"` +} + +func init() { + types.Add("pbm:PbmNonExistentHubs", reflect.TypeOf((*PbmNonExistentHubs)(nil)).Elem()) +} + +type PbmNonExistentHubsFault PbmNonExistentHubs + +func init() { + types.Add("pbm:PbmNonExistentHubsFault", reflect.TypeOf((*PbmNonExistentHubsFault)(nil)).Elem()) +} + +type PbmPersistenceBasedDataServiceInfo struct { + PbmLineOfServiceInfo + + CompatiblePersistenceSchemaNamespace []string `xml:"compatiblePersistenceSchemaNamespace,omitempty"` +} + +func init() { + types.Add("pbm:PbmPersistenceBasedDataServiceInfo", reflect.TypeOf((*PbmPersistenceBasedDataServiceInfo)(nil)).Elem()) +} + +type PbmPlacementCapabilityConstraintsRequirement struct { + PbmPlacementRequirement + + Constraints BasePbmCapabilityConstraints `xml:"constraints,typeattr"` +} + +func init() { + types.Add("pbm:PbmPlacementCapabilityConstraintsRequirement", reflect.TypeOf((*PbmPlacementCapabilityConstraintsRequirement)(nil)).Elem()) +} + +type PbmPlacementCapabilityProfileRequirement struct { + PbmPlacementRequirement + + ProfileId PbmProfileId `xml:"profileId"` +} + +func init() { + types.Add("pbm:PbmPlacementCapabilityProfileRequirement", reflect.TypeOf((*PbmPlacementCapabilityProfileRequirement)(nil)).Elem()) +} + +type PbmPlacementCompatibilityResult struct { + types.DynamicData + + Hub PbmPlacementHub `xml:"hub"` + MatchingResources []BasePbmPlacementMatchingResources `xml:"matchingResources,omitempty,typeattr"` + HowMany int64 `xml:"howMany,omitempty"` + Utilization []PbmPlacementResourceUtilization `xml:"utilization,omitempty"` + Warning []types.LocalizedMethodFault `xml:"warning,omitempty"` + Error []types.LocalizedMethodFault `xml:"error,omitempty"` +} + +func init() { + types.Add("pbm:PbmPlacementCompatibilityResult", reflect.TypeOf((*PbmPlacementCompatibilityResult)(nil)).Elem()) +} + +type PbmPlacementHub struct { + types.DynamicData + + HubType string `xml:"hubType"` + HubId string `xml:"hubId"` +} + +func init() { + types.Add("pbm:PbmPlacementHub", reflect.TypeOf((*PbmPlacementHub)(nil)).Elem()) +} + +type PbmPlacementMatchingReplicationResources struct { + PbmPlacementMatchingResources + + ReplicationGroup []types.ReplicationGroupId `xml:"replicationGroup,omitempty"` +} + +func init() { + types.Add("pbm:PbmPlacementMatchingReplicationResources", reflect.TypeOf((*PbmPlacementMatchingReplicationResources)(nil)).Elem()) +} + +type PbmPlacementMatchingResources struct { + types.DynamicData +} + +func init() { + types.Add("pbm:PbmPlacementMatchingResources", reflect.TypeOf((*PbmPlacementMatchingResources)(nil)).Elem()) +} + +type PbmPlacementRequirement struct { + types.DynamicData +} + +func init() { + types.Add("pbm:PbmPlacementRequirement", reflect.TypeOf((*PbmPlacementRequirement)(nil)).Elem()) +} + +type PbmPlacementResourceUtilization struct { + types.DynamicData + + Name PbmExtendedElementDescription `xml:"name"` + Description PbmExtendedElementDescription `xml:"description"` + AvailableBefore int64 `xml:"availableBefore,omitempty"` + AvailableAfter int64 `xml:"availableAfter,omitempty"` + Total int64 `xml:"total,omitempty"` +} + +func init() { + types.Add("pbm:PbmPlacementResourceUtilization", reflect.TypeOf((*PbmPlacementResourceUtilization)(nil)).Elem()) +} + +type PbmProfile struct { + types.DynamicData + + ProfileId PbmProfileId `xml:"profileId"` + Name string `xml:"name"` + Description string `xml:"description,omitempty"` + CreationTime time.Time `xml:"creationTime"` + CreatedBy string `xml:"createdBy"` + LastUpdatedTime time.Time `xml:"lastUpdatedTime"` + LastUpdatedBy string `xml:"lastUpdatedBy"` +} + +func init() { + types.Add("pbm:PbmProfile", reflect.TypeOf((*PbmProfile)(nil)).Elem()) +} + +type PbmProfileId struct { + types.DynamicData + + UniqueId string `xml:"uniqueId"` +} + +func init() { + types.Add("pbm:PbmProfileId", reflect.TypeOf((*PbmProfileId)(nil)).Elem()) +} + +type PbmProfileOperationOutcome struct { + types.DynamicData + + ProfileId PbmProfileId `xml:"profileId"` + Fault *types.LocalizedMethodFault `xml:"fault,omitempty"` +} + +func init() { + types.Add("pbm:PbmProfileOperationOutcome", reflect.TypeOf((*PbmProfileOperationOutcome)(nil)).Elem()) +} + +type PbmProfileResourceType struct { + types.DynamicData + + ResourceType string `xml:"resourceType"` +} + +func init() { + types.Add("pbm:PbmProfileResourceType", reflect.TypeOf((*PbmProfileResourceType)(nil)).Elem()) +} + +type PbmProfileType struct { + types.DynamicData + + UniqueId string `xml:"uniqueId"` +} + +func init() { + types.Add("pbm:PbmProfileType", reflect.TypeOf((*PbmProfileType)(nil)).Elem()) +} + +type PbmPropertyMismatchFault struct { + PbmCompatibilityCheckFault + + CapabilityInstanceId PbmCapabilityMetadataUniqueId `xml:"capabilityInstanceId"` + RequirementPropertyInstance PbmCapabilityPropertyInstance `xml:"requirementPropertyInstance"` +} + +func init() { + types.Add("pbm:PbmPropertyMismatchFault", reflect.TypeOf((*PbmPropertyMismatchFault)(nil)).Elem()) +} + +type PbmPropertyMismatchFaultFault BasePbmPropertyMismatchFault + +func init() { + types.Add("pbm:PbmPropertyMismatchFaultFault", reflect.TypeOf((*PbmPropertyMismatchFaultFault)(nil)).Elem()) +} + +type PbmQueryAssociatedEntities PbmQueryAssociatedEntitiesRequestType + +func init() { + types.Add("pbm:PbmQueryAssociatedEntities", reflect.TypeOf((*PbmQueryAssociatedEntities)(nil)).Elem()) +} + +type PbmQueryAssociatedEntitiesRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Profiles []PbmProfileId `xml:"profiles,omitempty"` +} + +func init() { + types.Add("pbm:PbmQueryAssociatedEntitiesRequestType", reflect.TypeOf((*PbmQueryAssociatedEntitiesRequestType)(nil)).Elem()) +} + +type PbmQueryAssociatedEntitiesResponse struct { + Returnval []PbmQueryProfileResult `xml:"returnval,omitempty"` +} + +type PbmQueryAssociatedEntity PbmQueryAssociatedEntityRequestType + +func init() { + types.Add("pbm:PbmQueryAssociatedEntity", reflect.TypeOf((*PbmQueryAssociatedEntity)(nil)).Elem()) +} + +type PbmQueryAssociatedEntityRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Profile PbmProfileId `xml:"profile"` + EntityType string `xml:"entityType,omitempty"` +} + +func init() { + types.Add("pbm:PbmQueryAssociatedEntityRequestType", reflect.TypeOf((*PbmQueryAssociatedEntityRequestType)(nil)).Elem()) +} + +type PbmQueryAssociatedEntityResponse struct { + Returnval []PbmServerObjectRef `xml:"returnval,omitempty"` +} + +type PbmQueryAssociatedProfile PbmQueryAssociatedProfileRequestType + +func init() { + types.Add("pbm:PbmQueryAssociatedProfile", reflect.TypeOf((*PbmQueryAssociatedProfile)(nil)).Elem()) +} + +type PbmQueryAssociatedProfileRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Entity PbmServerObjectRef `xml:"entity"` +} + +func init() { + types.Add("pbm:PbmQueryAssociatedProfileRequestType", reflect.TypeOf((*PbmQueryAssociatedProfileRequestType)(nil)).Elem()) +} + +type PbmQueryAssociatedProfileResponse struct { + Returnval []PbmProfileId `xml:"returnval,omitempty"` +} + +type PbmQueryAssociatedProfiles PbmQueryAssociatedProfilesRequestType + +func init() { + types.Add("pbm:PbmQueryAssociatedProfiles", reflect.TypeOf((*PbmQueryAssociatedProfiles)(nil)).Elem()) +} + +type PbmQueryAssociatedProfilesRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Entities []PbmServerObjectRef `xml:"entities"` +} + +func init() { + types.Add("pbm:PbmQueryAssociatedProfilesRequestType", reflect.TypeOf((*PbmQueryAssociatedProfilesRequestType)(nil)).Elem()) +} + +type PbmQueryAssociatedProfilesResponse struct { + Returnval []PbmQueryProfileResult `xml:"returnval,omitempty"` +} + +type PbmQueryByRollupComplianceStatus PbmQueryByRollupComplianceStatusRequestType + +func init() { + types.Add("pbm:PbmQueryByRollupComplianceStatus", reflect.TypeOf((*PbmQueryByRollupComplianceStatus)(nil)).Elem()) +} + +type PbmQueryByRollupComplianceStatusRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Status string `xml:"status"` +} + +func init() { + types.Add("pbm:PbmQueryByRollupComplianceStatusRequestType", reflect.TypeOf((*PbmQueryByRollupComplianceStatusRequestType)(nil)).Elem()) +} + +type PbmQueryByRollupComplianceStatusResponse struct { + Returnval []PbmServerObjectRef `xml:"returnval,omitempty"` +} + +type PbmQueryDefaultRequirementProfile PbmQueryDefaultRequirementProfileRequestType + +func init() { + types.Add("pbm:PbmQueryDefaultRequirementProfile", reflect.TypeOf((*PbmQueryDefaultRequirementProfile)(nil)).Elem()) +} + +type PbmQueryDefaultRequirementProfileRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Hub PbmPlacementHub `xml:"hub"` +} + +func init() { + types.Add("pbm:PbmQueryDefaultRequirementProfileRequestType", reflect.TypeOf((*PbmQueryDefaultRequirementProfileRequestType)(nil)).Elem()) +} + +type PbmQueryDefaultRequirementProfileResponse struct { + Returnval *PbmProfileId `xml:"returnval,omitempty"` +} + +type PbmQueryDefaultRequirementProfiles PbmQueryDefaultRequirementProfilesRequestType + +func init() { + types.Add("pbm:PbmQueryDefaultRequirementProfiles", reflect.TypeOf((*PbmQueryDefaultRequirementProfiles)(nil)).Elem()) +} + +type PbmQueryDefaultRequirementProfilesRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Datastores []PbmPlacementHub `xml:"datastores"` +} + +func init() { + types.Add("pbm:PbmQueryDefaultRequirementProfilesRequestType", reflect.TypeOf((*PbmQueryDefaultRequirementProfilesRequestType)(nil)).Elem()) +} + +type PbmQueryDefaultRequirementProfilesResponse struct { + Returnval []PbmDefaultProfileInfo `xml:"returnval"` +} + +type PbmQueryMatchingHub PbmQueryMatchingHubRequestType + +func init() { + types.Add("pbm:PbmQueryMatchingHub", reflect.TypeOf((*PbmQueryMatchingHub)(nil)).Elem()) +} + +type PbmQueryMatchingHubRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + HubsToSearch []PbmPlacementHub `xml:"hubsToSearch,omitempty"` + Profile PbmProfileId `xml:"profile"` +} + +func init() { + types.Add("pbm:PbmQueryMatchingHubRequestType", reflect.TypeOf((*PbmQueryMatchingHubRequestType)(nil)).Elem()) +} + +type PbmQueryMatchingHubResponse struct { + Returnval []PbmPlacementHub `xml:"returnval,omitempty"` +} + +type PbmQueryMatchingHubWithSpec PbmQueryMatchingHubWithSpecRequestType + +func init() { + types.Add("pbm:PbmQueryMatchingHubWithSpec", reflect.TypeOf((*PbmQueryMatchingHubWithSpec)(nil)).Elem()) +} + +type PbmQueryMatchingHubWithSpecRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + HubsToSearch []PbmPlacementHub `xml:"hubsToSearch,omitempty"` + CreateSpec PbmCapabilityProfileCreateSpec `xml:"createSpec"` +} + +func init() { + types.Add("pbm:PbmQueryMatchingHubWithSpecRequestType", reflect.TypeOf((*PbmQueryMatchingHubWithSpecRequestType)(nil)).Elem()) +} + +type PbmQueryMatchingHubWithSpecResponse struct { + Returnval []PbmPlacementHub `xml:"returnval,omitempty"` +} + +type PbmQueryProfile PbmQueryProfileRequestType + +func init() { + types.Add("pbm:PbmQueryProfile", reflect.TypeOf((*PbmQueryProfile)(nil)).Elem()) +} + +type PbmQueryProfileRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + ResourceType PbmProfileResourceType `xml:"resourceType"` + ProfileCategory string `xml:"profileCategory,omitempty"` +} + +func init() { + types.Add("pbm:PbmQueryProfileRequestType", reflect.TypeOf((*PbmQueryProfileRequestType)(nil)).Elem()) +} + +type PbmQueryProfileResponse struct { + Returnval []PbmProfileId `xml:"returnval,omitempty"` +} + +type PbmQueryProfileResult struct { + types.DynamicData + + Object PbmServerObjectRef `xml:"object"` + ProfileId []PbmProfileId `xml:"profileId,omitempty"` + Fault *types.LocalizedMethodFault `xml:"fault,omitempty"` +} + +func init() { + types.Add("pbm:PbmQueryProfileResult", reflect.TypeOf((*PbmQueryProfileResult)(nil)).Elem()) +} + +type PbmQueryReplicationGroupResult struct { + types.DynamicData + + Object PbmServerObjectRef `xml:"object"` + ReplicationGroupId *types.ReplicationGroupId `xml:"replicationGroupId,omitempty"` + Fault *types.LocalizedMethodFault `xml:"fault,omitempty"` +} + +func init() { + types.Add("pbm:PbmQueryReplicationGroupResult", reflect.TypeOf((*PbmQueryReplicationGroupResult)(nil)).Elem()) +} + +type PbmQueryReplicationGroups PbmQueryReplicationGroupsRequestType + +func init() { + types.Add("pbm:PbmQueryReplicationGroups", reflect.TypeOf((*PbmQueryReplicationGroups)(nil)).Elem()) +} + +type PbmQueryReplicationGroupsRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Entities []PbmServerObjectRef `xml:"entities,omitempty"` +} + +func init() { + types.Add("pbm:PbmQueryReplicationGroupsRequestType", reflect.TypeOf((*PbmQueryReplicationGroupsRequestType)(nil)).Elem()) +} + +type PbmQueryReplicationGroupsResponse struct { + Returnval []PbmQueryReplicationGroupResult `xml:"returnval,omitempty"` +} + +type PbmQuerySpaceStatsForStorageContainer PbmQuerySpaceStatsForStorageContainerRequestType + +func init() { + types.Add("pbm:PbmQuerySpaceStatsForStorageContainer", reflect.TypeOf((*PbmQuerySpaceStatsForStorageContainer)(nil)).Elem()) +} + +type PbmQuerySpaceStatsForStorageContainerRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Datastore PbmServerObjectRef `xml:"datastore"` + CapabilityProfileId []PbmProfileId `xml:"capabilityProfileId,omitempty"` +} + +func init() { + types.Add("pbm:PbmQuerySpaceStatsForStorageContainerRequestType", reflect.TypeOf((*PbmQuerySpaceStatsForStorageContainerRequestType)(nil)).Elem()) +} + +type PbmQuerySpaceStatsForStorageContainerResponse struct { + Returnval []PbmDatastoreSpaceStatistics `xml:"returnval,omitempty"` +} + +type PbmResetDefaultRequirementProfile PbmResetDefaultRequirementProfileRequestType + +func init() { + types.Add("pbm:PbmResetDefaultRequirementProfile", reflect.TypeOf((*PbmResetDefaultRequirementProfile)(nil)).Elem()) +} + +type PbmResetDefaultRequirementProfileRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + Profile *PbmProfileId `xml:"profile,omitempty"` +} + +func init() { + types.Add("pbm:PbmResetDefaultRequirementProfileRequestType", reflect.TypeOf((*PbmResetDefaultRequirementProfileRequestType)(nil)).Elem()) +} + +type PbmResetDefaultRequirementProfileResponse struct { +} + +type PbmResetVSanDefaultProfile PbmResetVSanDefaultProfileRequestType + +func init() { + types.Add("pbm:PbmResetVSanDefaultProfile", reflect.TypeOf((*PbmResetVSanDefaultProfile)(nil)).Elem()) +} + +type PbmResetVSanDefaultProfileRequestType struct { + This types.ManagedObjectReference `xml:"_this"` +} + +func init() { + types.Add("pbm:PbmResetVSanDefaultProfileRequestType", reflect.TypeOf((*PbmResetVSanDefaultProfileRequestType)(nil)).Elem()) +} + +type PbmResetVSanDefaultProfileResponse struct { +} + +type PbmResourceInUse struct { + PbmFault + + Type string `xml:"type,omitempty"` + Name string `xml:"name,omitempty"` +} + +func init() { + types.Add("pbm:PbmResourceInUse", reflect.TypeOf((*PbmResourceInUse)(nil)).Elem()) +} + +type PbmResourceInUseFault PbmResourceInUse + +func init() { + types.Add("pbm:PbmResourceInUseFault", reflect.TypeOf((*PbmResourceInUseFault)(nil)).Elem()) +} + +type PbmRetrieveContent PbmRetrieveContentRequestType + +func init() { + types.Add("pbm:PbmRetrieveContent", reflect.TypeOf((*PbmRetrieveContent)(nil)).Elem()) +} + +type PbmRetrieveContentRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + ProfileIds []PbmProfileId `xml:"profileIds"` +} + +func init() { + types.Add("pbm:PbmRetrieveContentRequestType", reflect.TypeOf((*PbmRetrieveContentRequestType)(nil)).Elem()) +} + +type PbmRetrieveContentResponse struct { + Returnval []BasePbmProfile `xml:"returnval,typeattr"` +} + +type PbmRetrieveServiceContent PbmRetrieveServiceContentRequestType + +func init() { + types.Add("pbm:PbmRetrieveServiceContent", reflect.TypeOf((*PbmRetrieveServiceContent)(nil)).Elem()) +} + +type PbmRetrieveServiceContentRequestType struct { + This types.ManagedObjectReference `xml:"_this"` +} + +func init() { + types.Add("pbm:PbmRetrieveServiceContentRequestType", reflect.TypeOf((*PbmRetrieveServiceContentRequestType)(nil)).Elem()) +} + +type PbmRetrieveServiceContentResponse struct { + Returnval PbmServiceInstanceContent `xml:"returnval"` +} + +type PbmRollupComplianceResult struct { + types.DynamicData + + OldestCheckTime time.Time `xml:"oldestCheckTime"` + Entity PbmServerObjectRef `xml:"entity"` + OverallComplianceStatus string `xml:"overallComplianceStatus"` + OverallComplianceTaskStatus string `xml:"overallComplianceTaskStatus,omitempty"` + Result []PbmComplianceResult `xml:"result,omitempty"` + ErrorCause []types.LocalizedMethodFault `xml:"errorCause,omitempty"` + ProfileMismatch bool `xml:"profileMismatch"` +} + +func init() { + types.Add("pbm:PbmRollupComplianceResult", reflect.TypeOf((*PbmRollupComplianceResult)(nil)).Elem()) +} + +type PbmServerObjectRef struct { + types.DynamicData + + ObjectType string `xml:"objectType"` + Key string `xml:"key"` + ServerUuid string `xml:"serverUuid,omitempty"` +} + +func init() { + types.Add("pbm:PbmServerObjectRef", reflect.TypeOf((*PbmServerObjectRef)(nil)).Elem()) +} + +type PbmServiceInstanceContent struct { + types.DynamicData + + AboutInfo PbmAboutInfo `xml:"aboutInfo"` + SessionManager types.ManagedObjectReference `xml:"sessionManager"` + CapabilityMetadataManager types.ManagedObjectReference `xml:"capabilityMetadataManager"` + ProfileManager types.ManagedObjectReference `xml:"profileManager"` + ComplianceManager types.ManagedObjectReference `xml:"complianceManager"` + PlacementSolver types.ManagedObjectReference `xml:"placementSolver"` + ReplicationManager *types.ManagedObjectReference `xml:"replicationManager,omitempty"` +} + +func init() { + types.Add("pbm:PbmServiceInstanceContent", reflect.TypeOf((*PbmServiceInstanceContent)(nil)).Elem()) +} + +type PbmUpdate PbmUpdateRequestType + +func init() { + types.Add("pbm:PbmUpdate", reflect.TypeOf((*PbmUpdate)(nil)).Elem()) +} + +type PbmUpdateRequestType struct { + This types.ManagedObjectReference `xml:"_this"` + ProfileId PbmProfileId `xml:"profileId"` + UpdateSpec PbmCapabilityProfileUpdateSpec `xml:"updateSpec"` +} + +func init() { + types.Add("pbm:PbmUpdateRequestType", reflect.TypeOf((*PbmUpdateRequestType)(nil)).Elem()) +} + +type PbmUpdateResponse struct { +} + +type PbmVaioDataServiceInfo struct { + PbmLineOfServiceInfo +} + +func init() { + types.Add("pbm:PbmVaioDataServiceInfo", reflect.TypeOf((*PbmVaioDataServiceInfo)(nil)).Elem()) +} diff --git a/vendor/github.com/vmware/govmomi/performance/manager.go b/vendor/github.com/vmware/govmomi/performance/manager.go new file mode 100644 index 00000000..736c9e19 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/performance/manager.go @@ -0,0 +1,431 @@ +/* +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 performance + +import ( + "context" + "fmt" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +// Manager wraps mo.PerformanceManager. +type Manager struct { + object.Common + + Sort bool + + pm struct { + sync.Mutex + *mo.PerformanceManager + } + + providerSummary struct { + sync.Mutex + m map[string]*types.PerfProviderSummary + } + + infoByName struct { + sync.Mutex + m map[string]*types.PerfCounterInfo + } + + infoByKey struct { + sync.Mutex + m map[int32]*types.PerfCounterInfo + } +} + +// NewManager creates a new Manager instance. +func NewManager(client *vim25.Client) *Manager { + m := Manager{ + Common: object.NewCommon(client, *client.ServiceContent.PerfManager), + } + + m.pm.PerformanceManager = new(mo.PerformanceManager) + + return &m +} + +// IntervalList wraps []types.PerfInterval. +type IntervalList []types.PerfInterval + +// Enabled returns a map with Level as the key and enabled PerfInterval.Name(s) as the value. +func (l IntervalList) Enabled() map[int32][]string { + enabled := make(map[int32][]string) + + for level := int32(0); level <= 4; level++ { + var names []string + + for _, interval := range l { + if interval.Enabled && interval.Level >= level { + names = append(names, interval.Name) + } + } + + enabled[level] = names + } + + return enabled +} + +// HistoricalInterval gets the PerformanceManager.HistoricalInterval property and wraps as an IntervalList. +func (m *Manager) HistoricalInterval(ctx context.Context) (IntervalList, error) { + var pm mo.PerformanceManager + + err := m.Properties(ctx, m.Reference(), []string{"historicalInterval"}, &pm) + if err != nil { + return nil, err + } + + return IntervalList(pm.HistoricalInterval), nil +} + +// CounterInfo gets the PerformanceManager.PerfCounter property. +// The property value is only collected once, subsequent calls return the cached value. +func (m *Manager) CounterInfo(ctx context.Context) ([]types.PerfCounterInfo, error) { + m.pm.Lock() + defer m.pm.Unlock() + + if len(m.pm.PerfCounter) == 0 { + err := m.Properties(ctx, m.Reference(), []string{"perfCounter"}, m.pm.PerformanceManager) + if err != nil { + return nil, err + } + } + + return m.pm.PerfCounter, nil +} + +// CounterInfoByName converts the PerformanceManager.PerfCounter property to a map, +// where key is types.PerfCounterInfo.Name(). +func (m *Manager) CounterInfoByName(ctx context.Context) (map[string]*types.PerfCounterInfo, error) { + m.infoByName.Lock() + defer m.infoByName.Unlock() + + if m.infoByName.m != nil { + return m.infoByName.m, nil + } + + info, err := m.CounterInfo(ctx) + if err != nil { + return nil, err + } + + m.infoByName.m = make(map[string]*types.PerfCounterInfo) + + for i := range info { + c := &info[i] + + m.infoByName.m[c.Name()] = c + } + + return m.infoByName.m, nil +} + +// CounterInfoByKey converts the PerformanceManager.PerfCounter property to a map, +// where key is types.PerfCounterInfo.Key. +func (m *Manager) CounterInfoByKey(ctx context.Context) (map[int32]*types.PerfCounterInfo, error) { + m.infoByKey.Lock() + defer m.infoByKey.Unlock() + + if m.infoByKey.m != nil { + return m.infoByKey.m, nil + } + + info, err := m.CounterInfo(ctx) + if err != nil { + return nil, err + } + + m.infoByKey.m = make(map[int32]*types.PerfCounterInfo) + + for i := range info { + c := &info[i] + + m.infoByKey.m[c.Key] = c + } + + return m.infoByKey.m, nil +} + +// ProviderSummary wraps the QueryPerfProviderSummary method, caching the value based on entity.Type. +func (m *Manager) ProviderSummary(ctx context.Context, entity types.ManagedObjectReference) (*types.PerfProviderSummary, error) { + m.providerSummary.Lock() + defer m.providerSummary.Unlock() + + if m.providerSummary.m == nil { + m.providerSummary.m = make(map[string]*types.PerfProviderSummary) + } + + s, ok := m.providerSummary.m[entity.Type] + if ok { + return s, nil + } + + req := types.QueryPerfProviderSummary{ + This: m.Reference(), + Entity: entity, + } + + res, err := methods.QueryPerfProviderSummary(ctx, m.Client(), &req) + if err != nil { + return nil, err + } + + s = &res.Returnval + + m.providerSummary.m[entity.Type] = s + + return s, nil +} + +type groupPerfCounterInfo struct { + info map[int32]*types.PerfCounterInfo + ids []types.PerfMetricId +} + +func (d groupPerfCounterInfo) Len() int { + return len(d.ids) +} + +func (d groupPerfCounterInfo) Less(i, j int) bool { + ci := d.ids[i].CounterId + cj := d.ids[j].CounterId + gi := d.info[ci].GroupInfo.GetElementDescription() + gj := d.info[cj].GroupInfo.GetElementDescription() + + return gi.Key < gj.Key +} + +func (d groupPerfCounterInfo) Swap(i, j int) { + d.ids[i], d.ids[j] = d.ids[j], d.ids[i] +} + +// MetricList wraps []types.PerfMetricId +type MetricList []types.PerfMetricId + +// ByKey converts MetricList to map, where key is types.PerfMetricId.CounterId / types.PerfCounterInfo.Key +func (l MetricList) ByKey() map[int32][]*types.PerfMetricId { + ids := make(map[int32][]*types.PerfMetricId, len(l)) + + for i := range l { + id := &l[i] + ids[id.CounterId] = append(ids[id.CounterId], id) + } + + return ids +} + +// AvailableMetric wraps the QueryAvailablePerfMetric method. +// The MetricList is sorted by PerfCounterInfo.GroupInfo.Key if Manager.Sort == true. +func (m *Manager) AvailableMetric(ctx context.Context, entity types.ManagedObjectReference, interval int32) (MetricList, error) { + req := types.QueryAvailablePerfMetric{ + This: m.Reference(), + Entity: entity.Reference(), + IntervalId: interval, + } + + res, err := methods.QueryAvailablePerfMetric(ctx, m.Client(), &req) + if err != nil { + return nil, err + } + + if m.Sort { + info, err := m.CounterInfoByKey(ctx) + if err != nil { + return nil, err + } + + sort.Sort(groupPerfCounterInfo{info, res.Returnval}) + } + + return MetricList(res.Returnval), nil +} + +// Query wraps the QueryPerf method. +func (m *Manager) Query(ctx context.Context, spec []types.PerfQuerySpec) ([]types.BasePerfEntityMetricBase, error) { + req := types.QueryPerf{ + This: m.Reference(), + QuerySpec: spec, + } + + res, err := methods.QueryPerf(ctx, m.Client(), &req) + if err != nil { + return nil, err + } + + return res.Returnval, nil +} + +// SampleByName uses the spec param as a template, constructing a []types.PerfQuerySpec for the given metrics and entities +// and invoking the Query method. +// The spec template can specify instances using the MetricId.Instance field, by default all instances are collected. +// The spec template MaxSample defaults to 1. +// If the spec template IntervalId is a historical interval and StartTime is not specified, +// the StartTime is set to the current time - (IntervalId * MaxSample). +func (m *Manager) SampleByName(ctx context.Context, spec types.PerfQuerySpec, metrics []string, entity []types.ManagedObjectReference) ([]types.BasePerfEntityMetricBase, error) { + info, err := m.CounterInfoByName(ctx) + if err != nil { + return nil, err + } + + var ids []types.PerfMetricId + + instances := spec.MetricId + if len(instances) == 0 { + // Default to all instances + instances = []types.PerfMetricId{{Instance: "*"}} + } + + for _, name := range metrics { + counter, ok := info[name] + if !ok { + return nil, fmt.Errorf("counter %q not found", name) + } + + for _, i := range instances { + ids = append(ids, types.PerfMetricId{CounterId: counter.Key, Instance: i.Instance}) + } + } + + spec.MetricId = ids + + if spec.MaxSample == 0 { + spec.MaxSample = 1 + } + + if spec.IntervalId >= 60 && spec.StartTime == nil { + // Need a StartTime to make use of history + now, err := methods.GetCurrentTime(ctx, m.Client()) + if err != nil { + return nil, err + } + + // Go back in time + x := spec.IntervalId * -1 * spec.MaxSample + t := now.Add(time.Duration(x) * time.Second) + spec.StartTime = &t + } + + var query []types.PerfQuerySpec + + for _, e := range entity { + spec.Entity = e.Reference() + query = append(query, spec) + } + + return m.Query(ctx, query) +} + +// MetricSeries contains the same data as types.PerfMetricIntSeries, but with the CounterId converted to Name. +type MetricSeries struct { + Name string + unit string + Instance string + Value []int64 +} + +func (s *MetricSeries) Format(val int64) string { + switch types.PerformanceManagerUnit(s.unit) { + case types.PerformanceManagerUnitPercent: + return strconv.FormatFloat(float64(val)/100.0, 'f', 2, 64) + default: + return strconv.FormatInt(val, 10) + } +} + +// ValueCSV converts the Value field to a CSV string +func (s *MetricSeries) ValueCSV() string { + vals := make([]string, len(s.Value)) + + for i := range s.Value { + vals[i] = s.Format(s.Value[i]) + } + + return strings.Join(vals, ",") +} + +// EntityMetric contains the same data as types.PerfEntityMetric, but with MetricSeries type for the Value field. +type EntityMetric struct { + Entity types.ManagedObjectReference + + SampleInfo []types.PerfSampleInfo + Value []MetricSeries +} + +// SampleInfoCSV converts the SampleInfo field to a CSV string +func (m *EntityMetric) SampleInfoCSV() string { + vals := make([]string, len(m.SampleInfo)*2) + + i := 0 + + for _, s := range m.SampleInfo { + vals[i] = s.Timestamp.Format(time.RFC3339) + i++ + vals[i] = strconv.Itoa(int(s.Interval)) + i++ + } + + return strings.Join(vals, ",") +} + +// ToMetricSeries converts []BasePerfEntityMetricBase to []EntityMetric +func (m *Manager) ToMetricSeries(ctx context.Context, series []types.BasePerfEntityMetricBase) ([]EntityMetric, error) { + counters, err := m.CounterInfoByKey(ctx) + if err != nil { + return nil, err + } + + var result []EntityMetric + + for i := range series { + var values []MetricSeries + s, ok := series[i].(*types.PerfEntityMetric) + if !ok { + panic(fmt.Errorf("expected type %T, got: %T", s, series[i])) + } + + for j := range s.Value { + v := s.Value[j].(*types.PerfMetricIntSeries) + + values = append(values, MetricSeries{ + Name: counters[v.Id.CounterId].Name(), + unit: counters[v.Id.CounterId].UnitInfo.GetElementDescription().Key, + Instance: v.Id.Instance, + Value: v.Value, + }) + } + + result = append(result, EntityMetric{ + Entity: s.Entity, + SampleInfo: s.SampleInfo, + Value: values, + }) + } + + return result, nil +} diff --git a/vendor/github.com/vmware/govmomi/property/collector.go b/vendor/github.com/vmware/govmomi/property/collector.go index 32a0e409..04a9e773 100644 --- a/vendor/github.com/vmware/govmomi/property/collector.go +++ b/vendor/github.com/vmware/govmomi/property/collector.go @@ -30,7 +30,7 @@ import ( // Collector models the PropertyCollector managed object. // // For more information, see: -// http://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vmodl.query.PropertyCollector.html +// http://pubs.vmware.com/vsphere-60/index.jsp?topic=%2Fcom.vmware.wssdk.apiref.doc%2Fvmodl.query.PropertyCollector.html // type Collector struct { roundTripper soap.RoundTripper @@ -164,9 +164,36 @@ func (p *Collector) Retrieve(ctx context.Context, objs []types.ManagedObjectRefe 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} diff --git a/vendor/github.com/vmware/govmomi/property/filter.go b/vendor/github.com/vmware/govmomi/property/filter.go new file mode 100644 index 00000000..8284b0c7 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/property/filter.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/property/wait.go b/vendor/github.com/vmware/govmomi/property/wait.go index 689477bf..fe847926 100644 --- a/vendor/github.com/vmware/govmomi/property/wait.go +++ b/vendor/github.com/vmware/govmomi/property/wait.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2015 VMware, Inc. All Rights Reserved. +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. @@ -22,73 +22,63 @@ import ( "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 - } +// WaitFilter provides helpers to construct a types.CreateFilter for use with property.Wait +type WaitFilter struct { + types.CreateFilter +} - // 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()) +// Add a new ObjectSpec and PropertySpec to the WaitFilter +func (f *WaitFilter) Add(obj types.ManagedObjectReference, kind string, ps []string, set ...types.BaseSelectionSpec) *WaitFilter { + spec := types.ObjectSpec{ + Obj: obj, + SelectSet: set, + } - req := types.CreateFilter{ - Spec: types.PropertyFilterSpec{ - ObjectSet: []types.ObjectSpec{ - { - Obj: obj, - }, - }, - PropSet: []types.PropertySpec{ - { - PathSet: ps, - Type: obj.Type, - }, - }, - }, + pset := types.PropertySpec{ + Type: kind, + PathSet: ps, } if len(ps) == 0 { - req.Spec.PropSet[0].All = types.NewBool(true) + pset.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) + f.Spec.ObjectSet = append(f.Spec.ObjectSet, spec) + + f.Spec.PropSet = append(f.Spec.PropSet, pset) + + return f +} + +// Wait creates a new WaitFilter and calls the specified function for each ObjectUpdate via WaitForUpdates +func Wait(ctx context.Context, c *Collector, obj types.ManagedObjectReference, ps []string, f func([]types.PropertyChange) bool) error { + filter := new(WaitFilter).Add(obj, obj.Type, ps) + + return WaitForUpdates(ctx, c, filter, func(updates []types.ObjectUpdate) bool { + for _, update := range updates { + if f(update.ChangeSet) { + return true + } + } + + return false }) } -// 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 +// WaitForUpdates 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 View's specified managed objects, the function +// 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). // -// 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 { +func WaitForUpdates(ctx context.Context, c *Collector, filter *WaitFilter, f func([]types.ObjectUpdate) bool) error { p, err := c.Create(ctx) if err != nil { return err @@ -98,38 +88,13 @@ func WaitForView(ctx context.Context, c *Collector, view types.ManagedObjectRefe // 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) + err = p.CreateFilter(ctx, filter.CreateFilter) 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) + res, err := p.WaitForUpdates(ctx, version) if err != nil { return err } @@ -142,12 +107,9 @@ func waitLoop(ctx context.Context, c *Collector, f func(types.ManagedObjectRefer version = res.Version for _, fs := range res.FilterSet { - for _, os := range fs.ObjectSet { - if f(os.Obj, os.ChangeSet) { - return nil - } + if f(fs.ObjectSet) { + return nil } } } - } diff --git a/vendor/github.com/vmware/govmomi/simulator/cluster_compute_resource.go b/vendor/github.com/vmware/govmomi/simulator/cluster_compute_resource.go new file mode 100644 index 00000000..d743c13a --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/cluster_compute_resource.go @@ -0,0 +1,99 @@ +/* +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 simulator + +import ( + "github.com/vmware/govmomi/simulator/esx" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type ClusterComputeResource struct { + mo.ClusterComputeResource +} + +type addHost struct { + *ClusterComputeResource + + req *types.AddHost_Task +} + +func (add *addHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) { + spec := add.req.Spec + + if spec.HostName == "" { + return nil, &types.NoHost{} + } + + host := NewHostSystem(esx.HostSystem) + host.Summary.Config.Name = spec.HostName + host.Name = host.Summary.Config.Name + host.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected + + cr := add.ClusterComputeResource + Map.PutEntity(cr, Map.NewEntity(host)) + + cr.Host = append(cr.Host, host.Reference()) + + if add.req.AsConnected { + host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected + } + + return host.Reference(), nil +} + +func (c *ClusterComputeResource) AddHostTask(add *types.AddHost_Task) soap.HasFault { + r := &methods.AddHost_TaskBody{} + + task := NewTask(&addHost{c, add}) + + r.Res = &types.AddHost_TaskResponse{ + Returnval: task.Self, + } + + task.Run() + + return r +} + +func CreateClusterComputeResource(f *Folder, name string, spec types.ClusterConfigSpecEx) (*ClusterComputeResource, types.BaseMethodFault) { + if e := Map.FindByName(name, f.ChildEntity); e != nil { + return nil, &types.DuplicateName{ + Name: e.Entity().Name, + Object: e.Reference(), + } + } + + cluster := &ClusterComputeResource{} + cluster.Name = name + + config := &types.ClusterConfigInfoEx{} + cluster.ConfigurationEx = config + + config.DrsConfig.Enabled = types.NewBool(true) + + pool := NewResourcePool() + Map.PutEntity(cluster, Map.NewEntity(pool)) + cluster.ResourcePool = &pool.Self + + f.putChild(cluster) + pool.Owner = cluster.Self + + return cluster, nil +} diff --git a/vendor/github.com/vmware/govmomi/simulator/datacenter.go b/vendor/github.com/vmware/govmomi/simulator/datacenter.go new file mode 100644 index 00000000..15b5d432 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/datacenter.go @@ -0,0 +1,76 @@ +/* +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 simulator + +import ( + "strings" + + "github.com/vmware/govmomi/simulator/esx" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +// Create Datacenter Folders. +// Every Datacenter has 4 inventory Folders: Vm, Host, Datastore and Network. +// The ESX folder child types are limited to 1 type. +// The VC folders have additional child types, including nested folders. +func createDatacenterFolders(dc *mo.Datacenter, isVC bool) { + folders := []struct { + ref *types.ManagedObjectReference + name string + types []string + }{ + {&dc.VmFolder, "vm", []string{"VirtualMachine", "VirtualApp", "Folder"}}, + {&dc.HostFolder, "host", []string{"ComputeResource", "Folder"}}, + {&dc.DatastoreFolder, "datastore", []string{"Datastore", "StoragePod", "Folder"}}, + {&dc.NetworkFolder, "network", []string{"Network", "DistributedVirtualSwitch", "Folder"}}, + } + + for _, f := range folders { + folder := &Folder{} + folder.Name = f.name + + if isVC { + folder.ChildType = f.types + e := Map.PutEntity(dc, folder) + + // propagate the generated morefs to Datacenter + ref := e.Reference() + f.ref.Type = ref.Type + f.ref.Value = ref.Value + } else { + folder.ChildType = f.types[:1] + folder.Self = *f.ref + Map.PutEntity(dc, folder) + } + } + + net := Map.Get(dc.NetworkFolder).(*Folder) + + for _, ref := range esx.Datacenter.Network { + // Add VM Network by default to each Datacenter + network := &mo.Network{} + network.Self = ref + network.Name = strings.Split(ref.Value, "-")[1] + network.Entity().Name = network.Name + if isVC { + network.Self.Value = "" // we want a different moid per-DC + } + + net.putChild(network) + } +} diff --git a/vendor/github.com/vmware/govmomi/simulator/datastore.go b/vendor/github.com/vmware/govmomi/simulator/datastore.go new file mode 100644 index 00000000..4098fbcf --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/datastore.go @@ -0,0 +1,76 @@ +/* +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 simulator + +import ( + "os/exec" + "strconv" + "strings" + "time" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type Datastore struct { + mo.Datastore +} + +func parseDatastorePath(dsPath string) (*object.DatastorePath, types.BaseMethodFault) { + var p object.DatastorePath + + if p.FromString(dsPath) { + return &p, nil + } + + return nil, &types.InvalidDatastorePath{DatastorePath: dsPath} +} + +func (ds *Datastore) RefreshDatastore(*types.RefreshDatastore) soap.HasFault { + r := &methods.RefreshDatastoreBody{} + + info := ds.Info.GetDatastoreInfo() + + // #nosec: Subprocess launching with variable + buf, err := exec.Command("df", "-k", info.Url).Output() + + if err != nil { + r.Fault_ = Fault(err.Error(), &types.HostConfigFault{}) + return r + } + + lines := strings.Split(string(buf), "\n") + columns := strings.Fields(lines[1]) + + used, _ := strconv.ParseInt(columns[2], 10, 64) + info.FreeSpace, _ = strconv.ParseInt(columns[3], 10, 64) + + info.FreeSpace *= 1024 + used *= 1024 + + ds.Summary.FreeSpace = info.FreeSpace + ds.Summary.Capacity = info.FreeSpace + used + + now := time.Now() + + info.Timestamp = &now + + return r +} diff --git a/vendor/github.com/vmware/govmomi/simulator/dvs.go b/vendor/github.com/vmware/govmomi/simulator/dvs.go new file mode 100644 index 00000000..4b62c55f --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/dvs.go @@ -0,0 +1,124 @@ +/* +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 simulator + +import ( + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type VmwareDistributedVirtualSwitch struct { + mo.VmwareDistributedVirtualSwitch +} + +func (s *VmwareDistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Task) soap.HasFault { + task := CreateTask(s, "addDVPortgroup", func(t *Task) (types.AnyType, types.BaseMethodFault) { + f := Map.getEntityParent(s, "Folder").(*Folder) + + for _, spec := range c.Spec { + pg := &mo.DistributedVirtualPortgroup{} + pg.Name = spec.Name + pg.Entity().Name = pg.Name + + if obj := Map.FindByName(pg.Name, f.ChildEntity); obj != nil { + return nil, &types.DuplicateName{ + Name: pg.Name, + Object: obj.Reference(), + } + } + + f.putChild(pg) + + pg.Key = pg.Self.Value + pg.Config.DistributedVirtualSwitch = &s.Self + + s.Portgroup = append(s.Portgroup, pg.Self) + s.Summary.PortgroupName = append(s.Summary.PortgroupName, pg.Name) + + for _, h := range s.Summary.HostMember { + pg.Host = AddReference(h, pg.Host) + host := Map.Get(h).(*HostSystem) + host.Network = append(host.Network, pg.Reference()) + } + } + + return nil, nil + }) + + task.Run() + + return &methods.AddDVPortgroup_TaskBody{ + Res: &types.AddDVPortgroup_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (s *VmwareDistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_Task) soap.HasFault { + task := CreateTask(s, "reconfigureDvs", func(t *Task) (types.AnyType, types.BaseMethodFault) { + spec := req.Spec.GetDVSConfigSpec() + + for _, member := range spec.Host { + h := Map.Get(member.Host) + if h == nil { + return nil, &types.ManagedObjectNotFound{Obj: member.Host} + } + + host := h.(*HostSystem) + + switch types.ConfigSpecOperation(member.Operation) { + case types.ConfigSpecOperationAdd: + if FindReference(host.Network, s.Self) != nil { + return nil, &types.AlreadyExists{Name: host.Name} + } + + host.Network = append(host.Network, s.Self) + host.Network = append(host.Network, s.Portgroup...) + s.Summary.HostMember = append(s.Summary.HostMember, member.Host) + + for _, ref := range s.Portgroup { + pg := Map.Get(ref).(*mo.DistributedVirtualPortgroup) + pg.Host = AddReference(member.Host, pg.Host) + } + case types.ConfigSpecOperationRemove: + if pg := FindReference(host.Network, s.Portgroup...); pg != nil { + return nil, &types.ResourceInUse{ + Type: pg.Type, + Name: pg.Value, + } + } + + host.Network = RemoveReference(s.Self, host.Network) + s.Summary.HostMember = RemoveReference(s.Self, s.Summary.HostMember) + case types.ConfigSpecOperationEdit: + return nil, &types.NotSupported{} + } + } + + return nil, nil + }) + + task.Run() + + return &methods.ReconfigureDvs_TaskBody{ + Res: &types.ReconfigureDvs_TaskResponse{ + Returnval: task.Self, + }, + } +} diff --git a/vendor/github.com/vmware/govmomi/simulator/entity.go b/vendor/github.com/vmware/govmomi/simulator/entity.go new file mode 100644 index 00000000..06c61411 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/entity.go @@ -0,0 +1,48 @@ +/* +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 simulator + +import ( + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +func RenameTask(e mo.Entity, r *types.Rename_Task) soap.HasFault { + task := CreateTask(e, "rename", func(t *Task) (types.AnyType, types.BaseMethodFault) { + obj := Map.Get(r.This).(mo.Entity).Entity() + + if parent, ok := Map.Get(*obj.Parent).(*Folder); ok { + if Map.FindByName(r.NewName, parent.ChildEntity) != nil { + return nil, &types.InvalidArgument{InvalidProperty: "name"} + } + } + + obj.Name = r.NewName + + return nil, nil + }) + + task.Run() + + return &methods.Rename_TaskBody{ + Res: &types.Rename_TaskResponse{ + Returnval: task.Self, + }, + } +} diff --git a/vendor/github.com/vmware/govmomi/simulator/esx/datacenter.go b/vendor/github.com/vmware/govmomi/simulator/esx/datacenter.go new file mode 100644 index 00000000..b202cbff --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/esx/datacenter.go @@ -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 esx + +import ( + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +var Datacenter = mo.Datacenter{ + ManagedEntity: mo.ManagedEntity{ + ExtensibleManagedObject: mo.ExtensibleManagedObject{ + Self: types.ManagedObjectReference{Type: "Datacenter", Value: "ha-datacenter"}, + Value: nil, + AvailableField: nil, + }, + Parent: (*types.ManagedObjectReference)(nil), + CustomValue: nil, + OverallStatus: "", + ConfigStatus: "", + ConfigIssue: nil, + EffectiveRole: nil, + Permission: nil, + Name: "ha-datacenter", + DisabledMethod: nil, + RecentTask: nil, + DeclaredAlarmState: nil, + TriggeredAlarmState: nil, + AlarmActionsEnabled: (*bool)(nil), + Tag: nil, + }, + VmFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-vm"}, + HostFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-host"}, + DatastoreFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-datastore"}, + NetworkFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-network"}, + Datastore: []types.ManagedObjectReference{ + {Type: "Datastore", Value: "57089c25-85e3ccd4-17b6-000c29d0beb3"}, + }, + Network: []types.ManagedObjectReference{ + {Type: "Network", Value: "HaNetwork-VM Network"}, + }, + Configuration: types.DatacenterConfigInfo{}, +} diff --git a/vendor/github.com/vmware/govmomi/simulator/esx/doc.go b/vendor/github.com/vmware/govmomi/simulator/esx/doc.go new file mode 100644 index 00000000..3c0f4035 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/esx/doc.go @@ -0,0 +1,20 @@ +/* +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 esx contains SOAP responses from an ESX server, captured using `govc ... -debug`. +*/ +package esx diff --git a/vendor/github.com/vmware/govmomi/simulator/esx/host_config_info.go b/vendor/github.com/vmware/govmomi/simulator/esx/host_config_info.go new file mode 100644 index 00000000..559a6eb3 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/esx/host_config_info.go @@ -0,0 +1,1088 @@ +/* +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 esx + +import "github.com/vmware/govmomi/vim25/types" + +var HostConfigInfo = types.HostConfigInfo{ + Host: types.ManagedObjectReference{Type: "HostSystem", Value: "ha-host"}, + Product: types.AboutInfo{ + Name: "VMware ESXi", + FullName: "VMware ESXi 6.5.0 build-5969303", + Vendor: "VMware, Inc.", + Version: "6.5.0", + Build: "5969303", + LocaleVersion: "INTL", + LocaleBuild: "000", + OsType: "vmnix-x86", + ProductLineId: "embeddedEsx", + ApiType: "HostAgent", + ApiVersion: "6.5", + InstanceUuid: "", + LicenseProductName: "VMware ESX Server", + LicenseProductVersion: "6.0", + }, + DeploymentInfo: &types.HostDeploymentInfo{ + BootedFromStatelessCache: types.NewBool(false), + }, + HyperThread: &types.HostHyperThreadScheduleInfo{ + Available: false, + Active: false, + Config: true, + }, + ConsoleReservation: (*types.ServiceConsoleReservationInfo)(nil), + VirtualMachineReservation: (*types.VirtualMachineMemoryReservationInfo)(nil), + StorageDevice: nil, + SystemFile: nil, + Network: &types.HostNetworkInfo{ + Vswitch: []types.HostVirtualSwitch{ + { + Name: "vSwitch0", + Key: "key-vim.host.VirtualSwitch-vSwitch0", + NumPorts: 1536, + NumPortsAvailable: 1530, + Mtu: 1500, + Portgroup: []string{"key-vim.host.PortGroup-VM Network", "key-vim.host.PortGroup-Management Network"}, + Pnic: []string{"key-vim.host.PhysicalNic-vmnic0"}, + Spec: types.HostVirtualSwitchSpec{ + NumPorts: 128, + Bridge: &types.HostVirtualSwitchBondBridge{ + HostVirtualSwitchBridge: types.HostVirtualSwitchBridge{}, + NicDevice: []string{"vmnic0"}, + Beacon: &types.HostVirtualSwitchBeaconConfig{ + Interval: 1, + }, + LinkDiscoveryProtocolConfig: &types.LinkDiscoveryProtocolConfig{ + Protocol: "cdp", + Operation: "listen", + }, + }, + Policy: &types.HostNetworkPolicy{ + Security: &types.HostNetworkSecurityPolicy{ + AllowPromiscuous: types.NewBool(false), + MacChanges: types.NewBool(true), + ForgedTransmits: types.NewBool(true), + }, + NicTeaming: &types.HostNicTeamingPolicy{ + Policy: "loadbalance_srcid", + ReversePolicy: types.NewBool(true), + NotifySwitches: types.NewBool(true), + RollingOrder: types.NewBool(false), + FailureCriteria: &types.HostNicFailureCriteria{ + CheckSpeed: "minimum", + Speed: 10, + CheckDuplex: types.NewBool(false), + FullDuplex: types.NewBool(false), + CheckErrorPercent: types.NewBool(false), + Percentage: 0, + CheckBeacon: types.NewBool(false), + }, + NicOrder: &types.HostNicOrderPolicy{ + ActiveNic: []string{"vmnic0"}, + StandbyNic: nil, + }, + }, + OffloadPolicy: &types.HostNetOffloadCapabilities{ + CsumOffload: types.NewBool(true), + TcpSegmentation: types.NewBool(true), + ZeroCopyXmit: types.NewBool(true), + }, + ShapingPolicy: &types.HostNetworkTrafficShapingPolicy{ + Enabled: types.NewBool(false), + AverageBandwidth: 0, + PeakBandwidth: 0, + BurstSize: 0, + }, + }, + Mtu: 0, + }, + }, + }, + ProxySwitch: nil, + Portgroup: []types.HostPortGroup{ + { + Key: "key-vim.host.PortGroup-VM Network", + Port: nil, + Vswitch: "key-vim.host.VirtualSwitch-vSwitch0", + ComputedPolicy: types.HostNetworkPolicy{ + Security: &types.HostNetworkSecurityPolicy{ + AllowPromiscuous: types.NewBool(false), + MacChanges: types.NewBool(true), + ForgedTransmits: types.NewBool(true), + }, + NicTeaming: &types.HostNicTeamingPolicy{ + Policy: "loadbalance_srcid", + ReversePolicy: types.NewBool(true), + NotifySwitches: types.NewBool(true), + RollingOrder: types.NewBool(false), + FailureCriteria: &types.HostNicFailureCriteria{ + CheckSpeed: "minimum", + Speed: 10, + CheckDuplex: types.NewBool(false), + FullDuplex: types.NewBool(false), + CheckErrorPercent: types.NewBool(false), + Percentage: 0, + CheckBeacon: types.NewBool(false), + }, + NicOrder: &types.HostNicOrderPolicy{ + ActiveNic: []string{"vmnic0"}, + StandbyNic: nil, + }, + }, + OffloadPolicy: &types.HostNetOffloadCapabilities{ + CsumOffload: types.NewBool(true), + TcpSegmentation: types.NewBool(true), + ZeroCopyXmit: types.NewBool(true), + }, + ShapingPolicy: &types.HostNetworkTrafficShapingPolicy{ + Enabled: types.NewBool(false), + AverageBandwidth: 0, + PeakBandwidth: 0, + BurstSize: 0, + }, + }, + Spec: types.HostPortGroupSpec{ + Name: "VM Network", + VlanId: 0, + VswitchName: "vSwitch0", + Policy: types.HostNetworkPolicy{ + Security: &types.HostNetworkSecurityPolicy{}, + NicTeaming: &types.HostNicTeamingPolicy{ + Policy: "", + ReversePolicy: (*bool)(nil), + NotifySwitches: (*bool)(nil), + RollingOrder: (*bool)(nil), + FailureCriteria: &types.HostNicFailureCriteria{}, + NicOrder: (*types.HostNicOrderPolicy)(nil), + }, + OffloadPolicy: &types.HostNetOffloadCapabilities{}, + ShapingPolicy: &types.HostNetworkTrafficShapingPolicy{}, + }, + }, + }, + { + Key: "key-vim.host.PortGroup-Management Network", + Port: []types.HostPortGroupPort{ + { + Key: "key-vim.host.PortGroup.Port-33554436", + Mac: []string{"00:0c:29:81:d8:a0"}, + Type: "host", + }, + }, + Vswitch: "key-vim.host.VirtualSwitch-vSwitch0", + ComputedPolicy: types.HostNetworkPolicy{ + Security: &types.HostNetworkSecurityPolicy{ + AllowPromiscuous: types.NewBool(false), + MacChanges: types.NewBool(true), + ForgedTransmits: types.NewBool(true), + }, + NicTeaming: &types.HostNicTeamingPolicy{ + Policy: "loadbalance_srcid", + ReversePolicy: types.NewBool(true), + NotifySwitches: types.NewBool(true), + RollingOrder: types.NewBool(false), + FailureCriteria: &types.HostNicFailureCriteria{ + CheckSpeed: "minimum", + Speed: 10, + CheckDuplex: types.NewBool(false), + FullDuplex: types.NewBool(false), + CheckErrorPercent: types.NewBool(false), + Percentage: 0, + CheckBeacon: types.NewBool(false), + }, + NicOrder: &types.HostNicOrderPolicy{ + ActiveNic: []string{"vmnic0"}, + StandbyNic: nil, + }, + }, + OffloadPolicy: &types.HostNetOffloadCapabilities{ + CsumOffload: types.NewBool(true), + TcpSegmentation: types.NewBool(true), + ZeroCopyXmit: types.NewBool(true), + }, + ShapingPolicy: &types.HostNetworkTrafficShapingPolicy{ + Enabled: types.NewBool(false), + AverageBandwidth: 0, + PeakBandwidth: 0, + BurstSize: 0, + }, + }, + Spec: types.HostPortGroupSpec{ + Name: "Management Network", + VlanId: 0, + VswitchName: "vSwitch0", + Policy: types.HostNetworkPolicy{ + Security: &types.HostNetworkSecurityPolicy{}, + NicTeaming: &types.HostNicTeamingPolicy{ + Policy: "loadbalance_srcid", + ReversePolicy: (*bool)(nil), + NotifySwitches: types.NewBool(true), + RollingOrder: types.NewBool(false), + FailureCriteria: &types.HostNicFailureCriteria{ + CheckSpeed: "", + Speed: 0, + CheckDuplex: (*bool)(nil), + FullDuplex: (*bool)(nil), + CheckErrorPercent: (*bool)(nil), + Percentage: 0, + CheckBeacon: types.NewBool(false), + }, + NicOrder: &types.HostNicOrderPolicy{ + ActiveNic: []string{"vmnic0"}, + StandbyNic: nil, + }, + }, + OffloadPolicy: &types.HostNetOffloadCapabilities{}, + ShapingPolicy: &types.HostNetworkTrafficShapingPolicy{}, + }, + }, + }, + }, + Pnic: []types.PhysicalNic{ + { + Key: "key-vim.host.PhysicalNic-vmnic0", + Device: "vmnic0", + Pci: "0000:0b:00.0", + Driver: "nvmxnet3", + LinkSpeed: &types.PhysicalNicLinkInfo{ + SpeedMb: 10000, + Duplex: true, + }, + ValidLinkSpecification: []types.PhysicalNicLinkInfo{ + { + SpeedMb: 10000, + Duplex: true, + }, + }, + Spec: types.PhysicalNicSpec{ + Ip: &types.HostIpConfig{}, + LinkSpeed: &types.PhysicalNicLinkInfo{ + SpeedMb: 10000, + Duplex: true, + }, + }, + WakeOnLanSupported: false, + Mac: "00:0c:29:81:d8:a0", + FcoeConfiguration: &types.FcoeConfig{ + PriorityClass: 3, + SourceMac: "00:0c:29:81:d8:a0", + VlanRange: []types.FcoeConfigVlanRange{ + {}, + }, + Capabilities: types.FcoeConfigFcoeCapabilities{ + PriorityClass: false, + SourceMacAddress: false, + VlanRange: true, + }, + FcoeActive: false, + }, + VmDirectPathGen2Supported: types.NewBool(false), + VmDirectPathGen2SupportedMode: "", + ResourcePoolSchedulerAllowed: types.NewBool(true), + ResourcePoolSchedulerDisallowedReason: nil, + AutoNegotiateSupported: types.NewBool(false), + }, + { + Key: "key-vim.host.PhysicalNic-vmnic1", + Device: "vmnic1", + Pci: "0000:13:00.0", + Driver: "nvmxnet3", + LinkSpeed: &types.PhysicalNicLinkInfo{ + SpeedMb: 10000, + Duplex: true, + }, + ValidLinkSpecification: []types.PhysicalNicLinkInfo{ + { + SpeedMb: 10000, + Duplex: true, + }, + }, + Spec: types.PhysicalNicSpec{ + Ip: &types.HostIpConfig{}, + LinkSpeed: &types.PhysicalNicLinkInfo{ + SpeedMb: 10000, + Duplex: true, + }, + }, + WakeOnLanSupported: false, + Mac: "00:0c:29:81:d8:aa", + FcoeConfiguration: &types.FcoeConfig{ + PriorityClass: 3, + SourceMac: "00:0c:29:81:d8:aa", + VlanRange: []types.FcoeConfigVlanRange{ + {}, + }, + Capabilities: types.FcoeConfigFcoeCapabilities{ + PriorityClass: false, + SourceMacAddress: false, + VlanRange: true, + }, + FcoeActive: false, + }, + VmDirectPathGen2Supported: types.NewBool(false), + VmDirectPathGen2SupportedMode: "", + ResourcePoolSchedulerAllowed: types.NewBool(true), + ResourcePoolSchedulerDisallowedReason: nil, + AutoNegotiateSupported: types.NewBool(false), + }, + }, + Vnic: []types.HostVirtualNic{ + { + Device: "vmk0", + Key: "key-vim.host.VirtualNic-vmk0", + Portgroup: "Management Network", + Spec: types.HostVirtualNicSpec{ + Ip: &types.HostIpConfig{ + Dhcp: true, + IpAddress: "127.0.0.1", + SubnetMask: "255.0.0.0", + IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil), + }, + Mac: "00:0c:29:81:d8:a0", + DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil), + Portgroup: "Management Network", + Mtu: 1500, + TsoEnabled: types.NewBool(true), + NetStackInstanceKey: "defaultTcpipStack", + OpaqueNetwork: (*types.HostVirtualNicOpaqueNetworkSpec)(nil), + ExternalId: "", + PinnedPnic: "", + IpRouteSpec: (*types.HostVirtualNicIpRouteSpec)(nil), + }, + Port: "key-vim.host.PortGroup.Port-33554436", + }, + }, + ConsoleVnic: nil, + DnsConfig: &types.HostDnsConfig{ + Dhcp: true, + VirtualNicDevice: "vmk0", + HostName: "localhost", + DomainName: "localdomain", + Address: []string{"8.8.8.8"}, + SearchDomain: []string{"localdomain"}, + }, + IpRouteConfig: &types.HostIpRouteConfig{ + DefaultGateway: "127.0.0.1", + GatewayDevice: "", + IpV6DefaultGateway: "", + IpV6GatewayDevice: "", + }, + ConsoleIpRouteConfig: nil, + RouteTableInfo: &types.HostIpRouteTableInfo{ + IpRoute: []types.HostIpRouteEntry{ + { + Network: "0.0.0.0", + PrefixLength: 0, + Gateway: "127.0.0.1", + DeviceName: "vmk0", + }, + { + Network: "127.0.0.0", + PrefixLength: 8, + Gateway: "0.0.0.0", + DeviceName: "vmk0", + }, + }, + Ipv6Route: nil, + }, + Dhcp: nil, + Nat: nil, + IpV6Enabled: types.NewBool(false), + AtBootIpV6Enabled: types.NewBool(false), + NetStackInstance: []types.HostNetStackInstance{ + { + Key: "vSphereProvisioning", + Name: "", + DnsConfig: &types.HostDnsConfig{}, + IpRouteConfig: &types.HostIpRouteConfig{}, + RequestedMaxNumberOfConnections: 11000, + CongestionControlAlgorithm: "newreno", + IpV6Enabled: types.NewBool(true), + RouteTableConfig: (*types.HostIpRouteTableConfig)(nil), + }, + { + Key: "vmotion", + Name: "", + DnsConfig: &types.HostDnsConfig{}, + IpRouteConfig: &types.HostIpRouteConfig{}, + RequestedMaxNumberOfConnections: 11000, + CongestionControlAlgorithm: "newreno", + IpV6Enabled: types.NewBool(true), + RouteTableConfig: (*types.HostIpRouteTableConfig)(nil), + }, + { + Key: "defaultTcpipStack", + Name: "defaultTcpipStack", + DnsConfig: &types.HostDnsConfig{ + Dhcp: true, + VirtualNicDevice: "vmk0", + HostName: "localhost", + DomainName: "localdomain", + Address: []string{"8.8.8.8"}, + SearchDomain: []string{"localdomain"}, + }, + IpRouteConfig: &types.HostIpRouteConfig{ + DefaultGateway: "127.0.0.1", + GatewayDevice: "", + IpV6DefaultGateway: "", + IpV6GatewayDevice: "", + }, + RequestedMaxNumberOfConnections: 11000, + CongestionControlAlgorithm: "newreno", + IpV6Enabled: types.NewBool(true), + RouteTableConfig: &types.HostIpRouteTableConfig{ + IpRoute: []types.HostIpRouteOp{ + { + ChangeOperation: "ignore", + Route: types.HostIpRouteEntry{ + Network: "0.0.0.0", + PrefixLength: 0, + Gateway: "127.0.0.1", + DeviceName: "vmk0", + }, + }, + { + ChangeOperation: "ignore", + Route: types.HostIpRouteEntry{ + Network: "127.0.0.0", + PrefixLength: 8, + Gateway: "0.0.0.0", + DeviceName: "vmk0", + }, + }, + }, + Ipv6Route: nil, + }, + }, + }, + OpaqueSwitch: nil, + OpaqueNetwork: nil, + }, + Vmotion: &types.HostVMotionInfo{ + NetConfig: &types.HostVMotionNetConfig{ + CandidateVnic: []types.HostVirtualNic{ + { + Device: "vmk0", + Key: "VMotionConfig.vmotion.key-vim.host.VirtualNic-vmk0", + Portgroup: "Management Network", + Spec: types.HostVirtualNicSpec{ + Ip: &types.HostIpConfig{ + Dhcp: true, + IpAddress: "127.0.0.1", + SubnetMask: "255.0.0.0", + IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil), + }, + Mac: "00:0c:29:81:d8:a0", + DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil), + Portgroup: "Management Network", + Mtu: 1500, + TsoEnabled: types.NewBool(true), + NetStackInstanceKey: "defaultTcpipStack", + OpaqueNetwork: (*types.HostVirtualNicOpaqueNetworkSpec)(nil), + ExternalId: "", + PinnedPnic: "", + IpRouteSpec: (*types.HostVirtualNicIpRouteSpec)(nil), + }, + Port: "", + }, + }, + SelectedVnic: "", + }, + IpConfig: (*types.HostIpConfig)(nil), + }, + VirtualNicManagerInfo: &types.HostVirtualNicManagerInfo{ + NetConfig: []types.VirtualNicManagerNetConfig{ + { + NicType: "faultToleranceLogging", + MultiSelectAllowed: true, + CandidateVnic: []types.HostVirtualNic{ + { + Device: "vmk0", + Key: "faultToleranceLogging.key-vim.host.VirtualNic-vmk0", + Portgroup: "Management Network", + Spec: types.HostVirtualNicSpec{ + Ip: &types.HostIpConfig{ + Dhcp: true, + IpAddress: "127.0.0.1", + SubnetMask: "255.0.0.0", + IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil), + }, + Mac: "00:0c:29:81:d8:a0", + DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil), + Portgroup: "Management Network", + Mtu: 1500, + TsoEnabled: types.NewBool(true), + NetStackInstanceKey: "defaultTcpipStack", + OpaqueNetwork: (*types.HostVirtualNicOpaqueNetworkSpec)(nil), + ExternalId: "", + PinnedPnic: "", + IpRouteSpec: (*types.HostVirtualNicIpRouteSpec)(nil), + }, + Port: "", + }, + }, + SelectedVnic: nil, + }, + { + NicType: "management", + MultiSelectAllowed: true, + CandidateVnic: []types.HostVirtualNic{ + { + Device: "vmk0", + Key: "management.key-vim.host.VirtualNic-vmk0", + Portgroup: "Management Network", + Spec: types.HostVirtualNicSpec{ + Ip: &types.HostIpConfig{ + Dhcp: true, + IpAddress: "127.0.0.1", + SubnetMask: "255.0.0.0", + IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil), + }, + Mac: "00:0c:29:81:d8:a0", + DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil), + Portgroup: "Management Network", + Mtu: 1500, + TsoEnabled: types.NewBool(true), + NetStackInstanceKey: "defaultTcpipStack", + OpaqueNetwork: (*types.HostVirtualNicOpaqueNetworkSpec)(nil), + ExternalId: "", + PinnedPnic: "", + IpRouteSpec: (*types.HostVirtualNicIpRouteSpec)(nil), + }, + Port: "", + }, + }, + SelectedVnic: []string{"management.key-vim.host.VirtualNic-vmk0"}, + }, + { + NicType: "vSphereProvisioning", + MultiSelectAllowed: true, + CandidateVnic: []types.HostVirtualNic{ + { + Device: "vmk0", + Key: "vSphereProvisioning.key-vim.host.VirtualNic-vmk0", + Portgroup: "Management Network", + Spec: types.HostVirtualNicSpec{ + Ip: &types.HostIpConfig{ + Dhcp: true, + IpAddress: "127.0.0.1", + SubnetMask: "255.0.0.0", + IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil), + }, + Mac: "00:0c:29:81:d8:a0", + DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil), + Portgroup: "Management Network", + Mtu: 1500, + TsoEnabled: types.NewBool(true), + NetStackInstanceKey: "defaultTcpipStack", + OpaqueNetwork: (*types.HostVirtualNicOpaqueNetworkSpec)(nil), + ExternalId: "", + PinnedPnic: "", + IpRouteSpec: (*types.HostVirtualNicIpRouteSpec)(nil), + }, + Port: "", + }, + }, + SelectedVnic: nil, + }, + { + NicType: "vSphereReplication", + MultiSelectAllowed: true, + CandidateVnic: []types.HostVirtualNic{ + { + Device: "vmk0", + Key: "vSphereReplication.key-vim.host.VirtualNic-vmk0", + Portgroup: "Management Network", + Spec: types.HostVirtualNicSpec{ + Ip: &types.HostIpConfig{ + Dhcp: true, + IpAddress: "127.0.0.1", + SubnetMask: "255.0.0.0", + IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil), + }, + Mac: "00:0c:29:81:d8:a0", + DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil), + Portgroup: "Management Network", + Mtu: 1500, + TsoEnabled: types.NewBool(true), + NetStackInstanceKey: "defaultTcpipStack", + OpaqueNetwork: (*types.HostVirtualNicOpaqueNetworkSpec)(nil), + ExternalId: "", + PinnedPnic: "", + IpRouteSpec: (*types.HostVirtualNicIpRouteSpec)(nil), + }, + Port: "", + }, + }, + SelectedVnic: nil, + }, + { + NicType: "vSphereReplicationNFC", + MultiSelectAllowed: true, + CandidateVnic: []types.HostVirtualNic{ + { + Device: "vmk0", + Key: "vSphereReplicationNFC.key-vim.host.VirtualNic-vmk0", + Portgroup: "Management Network", + Spec: types.HostVirtualNicSpec{ + Ip: &types.HostIpConfig{ + Dhcp: true, + IpAddress: "127.0.0.1", + SubnetMask: "255.0.0.0", + IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil), + }, + Mac: "00:0c:29:81:d8:a0", + DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil), + Portgroup: "Management Network", + Mtu: 1500, + TsoEnabled: types.NewBool(true), + NetStackInstanceKey: "defaultTcpipStack", + OpaqueNetwork: (*types.HostVirtualNicOpaqueNetworkSpec)(nil), + ExternalId: "", + PinnedPnic: "", + IpRouteSpec: (*types.HostVirtualNicIpRouteSpec)(nil), + }, + Port: "", + }, + }, + SelectedVnic: nil, + }, + { + NicType: "vmotion", + MultiSelectAllowed: true, + CandidateVnic: []types.HostVirtualNic{ + { + Device: "vmk0", + Key: "vmotion.key-vim.host.VirtualNic-vmk0", + Portgroup: "Management Network", + Spec: types.HostVirtualNicSpec{ + Ip: &types.HostIpConfig{ + Dhcp: true, + IpAddress: "127.0.0.1", + SubnetMask: "255.0.0.0", + IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil), + }, + Mac: "00:0c:29:81:d8:a0", + DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil), + Portgroup: "Management Network", + Mtu: 1500, + TsoEnabled: types.NewBool(true), + NetStackInstanceKey: "defaultTcpipStack", + OpaqueNetwork: (*types.HostVirtualNicOpaqueNetworkSpec)(nil), + ExternalId: "", + PinnedPnic: "", + IpRouteSpec: (*types.HostVirtualNicIpRouteSpec)(nil), + }, + Port: "", + }, + }, + SelectedVnic: nil, + }, + { + NicType: "vsan", + MultiSelectAllowed: true, + CandidateVnic: []types.HostVirtualNic{ + { + Device: "vmk0", + Key: "vsan.key-vim.host.VirtualNic-vmk0", + Portgroup: "Management Network", + Spec: types.HostVirtualNicSpec{ + Ip: &types.HostIpConfig{ + Dhcp: true, + IpAddress: "127.0.0.1", + SubnetMask: "255.0.0.0", + IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil), + }, + Mac: "00:0c:29:81:d8:a0", + DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil), + Portgroup: "Management Network", + Mtu: 1500, + TsoEnabled: types.NewBool(true), + NetStackInstanceKey: "defaultTcpipStack", + OpaqueNetwork: (*types.HostVirtualNicOpaqueNetworkSpec)(nil), + ExternalId: "", + PinnedPnic: "", + IpRouteSpec: (*types.HostVirtualNicIpRouteSpec)(nil), + }, + Port: "", + }, + }, + SelectedVnic: nil, + }, + { + NicType: "vsanWitness", + MultiSelectAllowed: true, + CandidateVnic: []types.HostVirtualNic{ + { + Device: "vmk0", + Key: "vsanWitness.key-vim.host.VirtualNic-vmk0", + Portgroup: "Management Network", + Spec: types.HostVirtualNicSpec{ + Ip: &types.HostIpConfig{ + Dhcp: true, + IpAddress: "127.0.0.1", + SubnetMask: "255.0.0.0", + IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil), + }, + Mac: "00:0c:29:81:d8:a0", + DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil), + Portgroup: "Management Network", + Mtu: 1500, + TsoEnabled: types.NewBool(true), + NetStackInstanceKey: "defaultTcpipStack", + OpaqueNetwork: (*types.HostVirtualNicOpaqueNetworkSpec)(nil), + ExternalId: "", + PinnedPnic: "", + IpRouteSpec: (*types.HostVirtualNicIpRouteSpec)(nil), + }, + Port: "", + }, + }, + SelectedVnic: nil, + }, + }, + }, + Capabilities: &types.HostNetCapabilities{ + CanSetPhysicalNicLinkSpeed: true, + SupportsNicTeaming: true, + NicTeamingPolicy: []string{"loadbalance_ip", "loadbalance_srcmac", "loadbalance_srcid", "failover_explicit"}, + SupportsVlan: true, + UsesServiceConsoleNic: false, + SupportsNetworkHints: true, + MaxPortGroupsPerVswitch: 0, + VswitchConfigSupported: true, + VnicConfigSupported: true, + IpRouteConfigSupported: true, + DnsConfigSupported: true, + DhcpOnVnicSupported: true, + IpV6Supported: types.NewBool(true), + }, + DatastoreCapabilities: &types.HostDatastoreSystemCapabilities{ + NfsMountCreationRequired: true, + NfsMountCreationSupported: true, + LocalDatastoreSupported: false, + VmfsExtentExpansionSupported: types.NewBool(true), + }, + OffloadCapabilities: &types.HostNetOffloadCapabilities{ + CsumOffload: types.NewBool(true), + TcpSegmentation: types.NewBool(true), + ZeroCopyXmit: types.NewBool(true), + }, + Service: &types.HostServiceInfo{ + Service: []types.HostService{ + { + Key: "DCUI", + Label: "Direct Console UI", + Required: false, + Uninstallable: false, + Running: true, + Ruleset: nil, + Policy: "on", + SourcePackage: &types.HostServiceSourcePackage{ + SourcePackageName: "esx-base", + Description: "This VIB contains all of the base functionality of vSphere ESXi.", + }, + }, + { + Key: "TSM", + Label: "ESXi Shell", + Required: false, + Uninstallable: false, + Running: false, + Ruleset: nil, + Policy: "off", + SourcePackage: &types.HostServiceSourcePackage{ + SourcePackageName: "esx-base", + Description: "This VIB contains all of the base functionality of vSphere ESXi.", + }, + }, + { + Key: "TSM-SSH", + Label: "SSH", + Required: false, + Uninstallable: false, + Running: false, + Ruleset: nil, + Policy: "off", + SourcePackage: &types.HostServiceSourcePackage{ + SourcePackageName: "esx-base", + Description: "This VIB contains all of the base functionality of vSphere ESXi.", + }, + }, + { + Key: "lbtd", + Label: "Load-Based Teaming Daemon", + Required: false, + Uninstallable: false, + Running: true, + Ruleset: nil, + Policy: "on", + SourcePackage: &types.HostServiceSourcePackage{ + SourcePackageName: "esx-base", + Description: "This VIB contains all of the base functionality of vSphere ESXi.", + }, + }, + { + Key: "lwsmd", + Label: "Active Directory Service", + Required: false, + Uninstallable: false, + Running: false, + Ruleset: nil, + Policy: "off", + SourcePackage: &types.HostServiceSourcePackage{ + SourcePackageName: "esx-base", + Description: "This VIB contains all of the base functionality of vSphere ESXi.", + }, + }, + { + Key: "ntpd", + Label: "NTP Daemon", + Required: false, + Uninstallable: false, + Running: false, + Ruleset: []string{"ntpClient"}, + Policy: "off", + SourcePackage: &types.HostServiceSourcePackage{ + SourcePackageName: "esx-base", + Description: "This VIB contains all of the base functionality of vSphere ESXi.", + }, + }, + { + Key: "pcscd", + Label: "PC/SC Smart Card Daemon", + Required: false, + Uninstallable: false, + Running: false, + Ruleset: nil, + Policy: "off", + SourcePackage: &types.HostServiceSourcePackage{ + SourcePackageName: "esx-base", + Description: "This VIB contains all of the base functionality of vSphere ESXi.", + }, + }, + { + Key: "sfcbd-watchdog", + Label: "CIM Server", + Required: false, + Uninstallable: false, + Running: false, + Ruleset: []string{"CIMHttpServer", "CIMHttpsServer"}, + Policy: "on", + SourcePackage: &types.HostServiceSourcePackage{ + SourcePackageName: "esx-base", + Description: "This VIB contains all of the base functionality of vSphere ESXi.", + }, + }, + { + Key: "snmpd", + Label: "SNMP Server", + Required: false, + Uninstallable: false, + Running: false, + Ruleset: []string{"snmp"}, + Policy: "on", + SourcePackage: &types.HostServiceSourcePackage{ + SourcePackageName: "esx-base", + Description: "This VIB contains all of the base functionality of vSphere ESXi.", + }, + }, + { + Key: "vmsyslogd", + Label: "Syslog Server", + Required: true, + Uninstallable: false, + Running: true, + Ruleset: nil, + Policy: "on", + SourcePackage: &types.HostServiceSourcePackage{ + SourcePackageName: "esx-base", + Description: "This VIB contains all of the base functionality of vSphere ESXi.", + }, + }, + { + Key: "vpxa", + Label: "VMware vCenter Agent", + Required: false, + Uninstallable: false, + Running: false, + Ruleset: []string{"vpxHeartbeats"}, + Policy: "on", + SourcePackage: &types.HostServiceSourcePackage{ + SourcePackageName: "esx-base", + Description: "This VIB contains all of the base functionality of vSphere ESXi.", + }, + }, + { + Key: "xorg", + Label: "X.Org Server", + Required: false, + Uninstallable: false, + Running: false, + Ruleset: nil, + Policy: "on", + SourcePackage: &types.HostServiceSourcePackage{ + SourcePackageName: "esx-xserver", + Description: "This VIB contains X Server used for virtual machine 3D hardware acceleration.", + }, + }, + }, + }, + Firewall: &HostFirewallInfo, + AutoStart: &types.HostAutoStartManagerConfig{ + Defaults: &types.AutoStartDefaults{ + Enabled: (*bool)(nil), + StartDelay: 120, + StopDelay: 120, + WaitForHeartbeat: types.NewBool(false), + StopAction: "PowerOff", + }, + PowerInfo: nil, + }, + ActiveDiagnosticPartition: &types.HostDiagnosticPartition{ + StorageType: "directAttached", + DiagnosticType: "singleHost", + Slots: -15, + Id: types.HostScsiDiskPartition{ + DiskName: "mpx.vmhba0:C0:T0:L0", + Partition: 9, + }, + }, + Option: nil, + OptionDef: nil, + Flags: &types.HostFlagInfo{}, + AdminDisabled: (*bool)(nil), + LockdownMode: "lockdownDisabled", + Ipmi: (*types.HostIpmiInfo)(nil), + SslThumbprintInfo: (*types.HostSslThumbprintInfo)(nil), + SslThumbprintData: nil, + Certificate: []uint8{0x31, 0x30}, + PciPassthruInfo: nil, + AuthenticationManagerInfo: &types.HostAuthenticationManagerInfo{ + AuthConfig: []types.BaseHostAuthenticationStoreInfo{ + &types.HostLocalAuthenticationInfo{ + HostAuthenticationStoreInfo: types.HostAuthenticationStoreInfo{ + Enabled: true, + }, + }, + &types.HostActiveDirectoryInfo{ + HostDirectoryStoreInfo: types.HostDirectoryStoreInfo{}, + JoinedDomain: "", + TrustedDomain: nil, + DomainMembershipStatus: "", + SmartCardAuthenticationEnabled: types.NewBool(false), + }, + }, + }, + FeatureVersion: nil, + PowerSystemCapability: &types.PowerSystemCapability{ + AvailablePolicy: []types.HostPowerPolicy{ + { + Key: 1, + Name: "PowerPolicy.static.name", + ShortName: "static", + Description: "PowerPolicy.static.description", + }, + { + Key: 2, + Name: "PowerPolicy.dynamic.name", + ShortName: "dynamic", + Description: "PowerPolicy.dynamic.description", + }, + { + Key: 3, + Name: "PowerPolicy.low.name", + ShortName: "low", + Description: "PowerPolicy.low.description", + }, + { + Key: 4, + Name: "PowerPolicy.custom.name", + ShortName: "custom", + Description: "PowerPolicy.custom.description", + }, + }, + }, + PowerSystemInfo: &types.PowerSystemInfo{ + CurrentPolicy: types.HostPowerPolicy{ + Key: 2, + Name: "PowerPolicy.dynamic.name", + ShortName: "dynamic", + Description: "PowerPolicy.dynamic.description", + }, + }, + CacheConfigurationInfo: []types.HostCacheConfigurationInfo{ + { + Key: types.ManagedObjectReference{Type: "Datastore", Value: "5980f676-21a5db76-9eef-000c2981d8a0"}, + SwapSize: 0, + }, + }, + WakeOnLanCapable: types.NewBool(false), + FeatureCapability: nil, + MaskedFeatureCapability: nil, + VFlashConfigInfo: nil, + VsanHostConfig: &types.VsanHostConfigInfo{ + Enabled: types.NewBool(false), + HostSystem: &types.ManagedObjectReference{Type: "HostSystem", Value: "ha-host"}, + ClusterInfo: &types.VsanHostConfigInfoClusterInfo{}, + StorageInfo: &types.VsanHostConfigInfoStorageInfo{ + AutoClaimStorage: types.NewBool(false), + DiskMapping: nil, + DiskMapInfo: nil, + ChecksumEnabled: (*bool)(nil), + }, + NetworkInfo: &types.VsanHostConfigInfoNetworkInfo{}, + FaultDomainInfo: &types.VsanHostFaultDomainInfo{}, + }, + DomainList: nil, + ScriptCheckSum: nil, + HostConfigCheckSum: nil, + GraphicsInfo: nil, + SharedPassthruGpuTypes: nil, + GraphicsConfig: &types.HostGraphicsConfig{ + HostDefaultGraphicsType: "shared", + SharedPassthruAssignmentPolicy: "performance", + DeviceType: nil, + }, + IoFilterInfo: []types.HostIoFilterInfo{ + { + IoFilterInfo: types.IoFilterInfo{ + Id: "VMW_spm_1.0.0", + Name: "spm", + Vendor: "VMW", + Version: "1.0.230", + Type: "datastoreIoControl", + Summary: "VMware Storage I/O Control", + ReleaseDate: "2016-07-21", + }, + Available: true, + }, + { + IoFilterInfo: types.IoFilterInfo{ + Id: "VMW_vmwarevmcrypt_1.0.0", + Name: "vmwarevmcrypt", + Vendor: "VMW", + Version: "1.0.0", + Type: "encryption", + Summary: "VMcrypt IO Filter", + ReleaseDate: "2016-07-21", + }, + Available: true, + }, + }, + SriovDevicePool: nil, +} diff --git a/vendor/github.com/vmware/govmomi/simulator/esx/host_firewall_system.go b/vendor/github.com/vmware/govmomi/simulator/esx/host_firewall_system.go new file mode 100644 index 00000000..9c890bfa --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/esx/host_firewall_system.go @@ -0,0 +1,1422 @@ +/* +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 esx + +import "github.com/vmware/govmomi/vim25/types" + +var HostFirewallInfo = types.HostFirewallInfo{ + DynamicData: types.DynamicData{}, + DefaultPolicy: types.HostFirewallDefaultPolicy{ + DynamicData: types.DynamicData{}, + IncomingBlocked: types.NewBool(true), + OutgoingBlocked: types.NewBool(true), + }, + Ruleset: []types.HostFirewallRuleset{ + { + DynamicData: types.DynamicData{}, + Key: "CIMHttpServer", + Label: "CIM Server", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 5988, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "sfcbd-watchdog", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "CIMHttpsServer", + Label: "CIM Secure Server", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 5989, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "sfcbd-watchdog", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "CIMSLP", + Label: "CIM SLP", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 427, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 427, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 427, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 427, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "DHCPv6", + Label: "DHCPv6", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 547, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 546, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 547, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 546, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "udp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "DVFilter", + Label: "DVFilter", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 2222, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "DVSSync", + Label: "DVSSync", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 8302, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 8301, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 8301, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 8302, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "udp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "HBR", + Label: "HBR", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 31031, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 44046, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "NFC", + Label: "NFC", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 902, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 902, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "WOL", + Label: "WOL", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 9, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "activeDirectoryAll", + Label: "Active Directory All", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 88, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 88, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 123, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 137, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 139, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 389, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 389, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 445, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 464, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 464, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 3268, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 7476, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 2020, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "cmmds", + Label: "Virtual SAN Clustering Service", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 12345, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 23451, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 12345, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 23451, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 12321, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 12321, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "dhcp", + Label: "DHCP Client", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 68, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 68, + EndPort: 0, + Direction: "outbound", + PortType: "src", + Protocol: "udp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "dns", + Label: "DNS Client", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 53, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 53, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 53, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "esxupdate", + Label: "esxupdate", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 443, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "faultTolerance", + Label: "Fault Tolerance", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 80, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 8300, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 8300, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "ftpClient", + Label: "FTP Client", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 21, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 20, + EndPort: 0, + Direction: "inbound", + PortType: "src", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "gdbserver", + Label: "gdbserver", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 1000, + EndPort: 9999, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 50000, + EndPort: 50999, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "httpClient", + Label: "httpClient", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 80, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 443, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "iSCSI", + Label: "Software iSCSI Client", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 3260, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "iofiltervp", + Label: "iofiltervp", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 9080, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "ipfam", + Label: "NSX Distributed Logical Router Service", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 6999, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 6999, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "nfs41Client", + Label: "nfs41Client", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 0, + EndPort: 65535, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "nfsClient", + Label: "NFS Client", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 0, + EndPort: 65535, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "ntpClient", + Label: "NTP Client", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 123, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + }, + Service: "ntpd", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "pvrdma", + Label: "pvrdma", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 28250, + EndPort: 28761, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 28250, + EndPort: 28761, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "rabbitmqproxy", + Label: "rabbitmqproxy", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 5671, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "rdt", + Label: "Virtual SAN Transport", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 2233, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 2233, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "remoteSerialPort", + Label: "VM serial port connected over network", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 0, + EndPort: 65535, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 23, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 1024, + EndPort: 65535, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "snmp", + Label: "SNMP Server", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 161, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "udp", + }, + }, + Service: "snmpd", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "sshClient", + Label: "SSH Client", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 22, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "sshServer", + Label: "SSH Server", + Required: true, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 22, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "syslog", + Label: "syslog", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 514, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 514, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 1514, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "updateManager", + Label: "vCenter Update Manager", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 80, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 9000, + EndPort: 9100, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "vMotion", + Label: "vMotion", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 8000, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 8000, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "vSPC", + Label: "VM serial port connected to vSPC", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 0, + EndPort: 65535, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "vSphereClient", + Label: "vSphere Web Client", + Required: true, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 902, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 443, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "vpxHeartbeats", + Label: "VMware vCenter Agent", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 902, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + }, + Service: "vpxa", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "vsanEncryption", + Label: "vsanEncryption", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 0, + EndPort: 65535, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "vsanhealth-multicasttest", + Label: "vsanhealth-multicasttest", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 5001, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "udp", + }, + { + DynamicData: types.DynamicData{}, + Port: 5001, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "udp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "vsanvp", + Label: "vsanvp", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 8080, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + { + DynamicData: types.DynamicData{}, + Port: 8080, + EndPort: 0, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "vvold", + Label: "vvold", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 0, + EndPort: 65535, + Direction: "outbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: false, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + { + DynamicData: types.DynamicData{}, + Key: "webAccess", + Label: "vSphere Web Access", + Required: false, + Rule: []types.HostFirewallRule{ + { + DynamicData: types.DynamicData{}, + Port: 80, + EndPort: 0, + Direction: "inbound", + PortType: "dst", + Protocol: "tcp", + }, + }, + Service: "", + Enabled: true, + AllowedHosts: &types.HostFirewallRulesetIpList{ + DynamicData: types.DynamicData{}, + IpAddress: nil, + IpNetwork: nil, + AllIp: true, + }, + }, + }, +} diff --git a/vendor/github.com/vmware/govmomi/simulator/esx/host_hardware_info.go b/vendor/github.com/vmware/govmomi/simulator/esx/host_hardware_info.go new file mode 100644 index 00000000..a1c9ad73 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/esx/host_hardware_info.go @@ -0,0 +1,861 @@ +/* +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 esx + +import ( + "time" + + "github.com/vmware/govmomi/vim25/types" +) + +var HostHardwareInfo = &types.HostHardwareInfo{ + SystemInfo: types.HostSystemInfo{ + Vendor: "VMware, Inc.", + Model: "VMware Virtual Platform", + Uuid: "e88d4d56-9f1e-3ea1-71fa-13a8e1a7fd70", + OtherIdentifyingInfo: []types.HostSystemIdentificationInfo{ + { + IdentifierValue: " No Asset Tag", + IdentifierType: &types.ElementDescription{ + Description: types.Description{ + Label: "Asset Tag", + Summary: "Asset tag of the system", + }, + Key: "AssetTag", + }, + }, + { + IdentifierValue: "[MS_VM_CERT/SHA1/27d66596a61c48dd3dc7216fd715126e33f59ae7]", + IdentifierType: &types.ElementDescription{ + Description: types.Description{ + Label: "OEM specific string", + Summary: "OEM specific string", + }, + Key: "OemSpecificString", + }, + }, + { + IdentifierValue: "Welcome to the Virtual Machine", + IdentifierType: &types.ElementDescription{ + Description: types.Description{ + Label: "OEM specific string", + Summary: "OEM specific string", + }, + Key: "OemSpecificString", + }, + }, + { + IdentifierValue: "VMware-56 4d 8d e8 1e 9f a1 3e-71 fa 13 a8 e1 a7 fd 70", + IdentifierType: &types.ElementDescription{ + Description: types.Description{ + Label: "Service tag", + Summary: "Service tag of the system", + }, + Key: "ServiceTag", + }, + }, + }, + }, + CpuPowerManagementInfo: &types.HostCpuPowerManagementInfo{ + CurrentPolicy: "Balanced", + HardwareSupport: "", + }, + CpuInfo: types.HostCpuInfo{ + NumCpuPackages: 2, + NumCpuCores: 2, + NumCpuThreads: 2, + Hz: 3591345000, + }, + CpuPkg: []types.HostCpuPackage{ + { + Index: 0, + Vendor: "intel", + Hz: 3591345000, + BusHz: 115849838, + Description: "Intel(R) Xeon(R) CPU E5-1620 0 @ 3.60GHz", + ThreadId: []int16{0}, + CpuFeature: []types.HostCpuIdInfo{ + { + Level: 0, + Vendor: "", + Eax: "0000:0000:0000:0000:0000:0000:0000:1101", + Ebx: "0111:0101:0110:1110:0110:0101:0100:0111", + Ecx: "0110:1100:0110:0101:0111:0100:0110:1110", + Edx: "0100:1001:0110:0101:0110:1110:0110:1001", + }, + { + Level: 1, + Vendor: "", + Eax: "0000:0000:0000:0010:0000:0110:1101:0111", + Ebx: "0000:0000:0000:0001:0000:1000:0000:0000", + Ecx: "1001:0111:1011:1010:0010:0010:0010:1011", + Edx: "0000:1111:1010:1011:1111:1011:1111:1111", + }, + { + Level: -2147483648, + Vendor: "", + Eax: "1000:0000:0000:0000:0000:0000:0000:1000", + Ebx: "0000:0000:0000:0000:0000:0000:0000:0000", + Ecx: "0000:0000:0000:0000:0000:0000:0000:0000", + Edx: "0000:0000:0000:0000:0000:0000:0000:0000", + }, + { + Level: -2147483647, + Vendor: "", + Eax: "0000:0000:0000:0000:0000:0000:0000:0000", + Ebx: "0000:0000:0000:0000:0000:0000:0000:0000", + Ecx: "0000:0000:0000:0000:0000:0000:0000:0001", + Edx: "0010:1000:0001:0000:0000:1000:0000:0000", + }, + { + Level: -2147483640, + Vendor: "", + Eax: "0000:0000:0000:0000:0011:0000:0010:1010", + Ebx: "0000:0000:0000:0000:0000:0000:0000:0000", + Ecx: "0000:0000:0000:0000:0000:0000:0000:0000", + Edx: "0000:0000:0000:0000:0000:0000:0000:0000", + }, + }, + }, + { + Index: 1, + Vendor: "intel", + Hz: 3591345000, + BusHz: 115849838, + Description: "Intel(R) Xeon(R) CPU E5-1620 0 @ 3.60GHz", + ThreadId: []int16{1}, + CpuFeature: []types.HostCpuIdInfo{ + { + Level: 0, + Vendor: "", + Eax: "0000:0000:0000:0000:0000:0000:0000:1101", + Ebx: "0111:0101:0110:1110:0110:0101:0100:0111", + Ecx: "0110:1100:0110:0101:0111:0100:0110:1110", + Edx: "0100:1001:0110:0101:0110:1110:0110:1001", + }, + { + Level: 1, + Vendor: "", + Eax: "0000:0000:0000:0010:0000:0110:1101:0111", + Ebx: "0000:0010:0000:0001:0000:1000:0000:0000", + Ecx: "1001:0111:1011:1010:0010:0010:0010:1011", + Edx: "0000:1111:1010:1011:1111:1011:1111:1111", + }, + { + Level: -2147483648, + Vendor: "", + Eax: "1000:0000:0000:0000:0000:0000:0000:1000", + Ebx: "0000:0000:0000:0000:0000:0000:0000:0000", + Ecx: "0000:0000:0000:0000:0000:0000:0000:0000", + Edx: "0000:0000:0000:0000:0000:0000:0000:0000", + }, + { + Level: -2147483647, + Vendor: "", + Eax: "0000:0000:0000:0000:0000:0000:0000:0000", + Ebx: "0000:0000:0000:0000:0000:0000:0000:0000", + Ecx: "0000:0000:0000:0000:0000:0000:0000:0001", + Edx: "0010:1000:0001:0000:0000:1000:0000:0000", + }, + { + Level: -2147483640, + Vendor: "", + Eax: "0000:0000:0000:0000:0011:0000:0010:1010", + Ebx: "0000:0000:0000:0000:0000:0000:0000:0000", + Ecx: "0000:0000:0000:0000:0000:0000:0000:0000", + Edx: "0000:0000:0000:0000:0000:0000:0000:0000", + }, + }, + }, + }, + MemorySize: 4294430720, + NumaInfo: &types.HostNumaInfo{ + Type: "NUMA", + NumNodes: 1, + NumaNode: []types.HostNumaNode{ + { + TypeId: 0x0, + CpuID: []int16{1, 0}, + MemoryRangeBegin: 4294967296, + MemoryRangeLength: 1073741824, + }, + }, + }, + SmcPresent: types.NewBool(false), + PciDevice: []types.HostPciDevice{ + { + Id: "0000:00:00.0", + ClassId: 1536, + Bus: 0x0, + Slot: 0x0, + Function: 0x0, + VendorId: -32634, + SubVendorId: 5549, + VendorName: "Intel Corporation", + DeviceId: 29072, + SubDeviceId: 6518, + ParentBridge: "", + DeviceName: "Virtual Machine Chipset", + }, + { + Id: "0000:00:01.0", + ClassId: 1540, + Bus: 0x0, + Slot: 0x1, + Function: 0x0, + VendorId: -32634, + SubVendorId: 0, + VendorName: "Intel Corporation", + DeviceId: 29073, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "440BX/ZX/DX - 82443BX/ZX/DX AGP bridge", + }, + { + Id: "0000:00:07.0", + ClassId: 1537, + Bus: 0x0, + Slot: 0x7, + Function: 0x0, + VendorId: -32634, + SubVendorId: 5549, + VendorName: "Intel Corporation", + DeviceId: 28944, + SubDeviceId: 6518, + ParentBridge: "", + DeviceName: "Virtual Machine Chipset", + }, + { + Id: "0000:00:07.1", + ClassId: 257, + Bus: 0x0, + Slot: 0x7, + Function: 0x1, + VendorId: -32634, + SubVendorId: 5549, + VendorName: "Intel Corporation", + DeviceId: 28945, + SubDeviceId: 6518, + ParentBridge: "", + DeviceName: "PIIX4 for 430TX/440BX/MX IDE Controller", + }, + { + Id: "0000:00:07.3", + ClassId: 1664, + Bus: 0x0, + Slot: 0x7, + Function: 0x3, + VendorId: -32634, + SubVendorId: 5549, + VendorName: "Intel Corporation", + DeviceId: 28947, + SubDeviceId: 6518, + ParentBridge: "", + DeviceName: "Virtual Machine Chipset", + }, + { + Id: "0000:00:07.7", + ClassId: 2176, + Bus: 0x0, + Slot: 0x7, + Function: 0x7, + VendorId: 5549, + SubVendorId: 5549, + VendorName: "VMware", + DeviceId: 1856, + SubDeviceId: 1856, + ParentBridge: "", + DeviceName: "Virtual Machine Communication Interface", + }, + { + Id: "0000:00:0f.0", + ClassId: 768, + Bus: 0x0, + Slot: 0xf, + Function: 0x0, + VendorId: 5549, + SubVendorId: 5549, + VendorName: "VMware", + DeviceId: 1029, + SubDeviceId: 1029, + ParentBridge: "", + DeviceName: "SVGA II Adapter", + }, + { + Id: "0000:00:11.0", + ClassId: 1540, + Bus: 0x0, + Slot: 0x11, + Function: 0x0, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1936, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI bridge", + }, + { + Id: "0000:00:15.0", + ClassId: 1540, + Bus: 0x0, + Slot: 0x15, + Function: 0x0, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:15.1", + ClassId: 1540, + Bus: 0x0, + Slot: 0x15, + Function: 0x1, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:15.2", + ClassId: 1540, + Bus: 0x0, + Slot: 0x15, + Function: 0x2, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:15.3", + ClassId: 1540, + Bus: 0x0, + Slot: 0x15, + Function: 0x3, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:15.4", + ClassId: 1540, + Bus: 0x0, + Slot: 0x15, + Function: 0x4, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:15.5", + ClassId: 1540, + Bus: 0x0, + Slot: 0x15, + Function: 0x5, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:15.6", + ClassId: 1540, + Bus: 0x0, + Slot: 0x15, + Function: 0x6, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:15.7", + ClassId: 1540, + Bus: 0x0, + Slot: 0x15, + Function: 0x7, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:16.0", + ClassId: 1540, + Bus: 0x0, + Slot: 0x16, + Function: 0x0, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:16.1", + ClassId: 1540, + Bus: 0x0, + Slot: 0x16, + Function: 0x1, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:16.2", + ClassId: 1540, + Bus: 0x0, + Slot: 0x16, + Function: 0x2, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:16.3", + ClassId: 1540, + Bus: 0x0, + Slot: 0x16, + Function: 0x3, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:16.4", + ClassId: 1540, + Bus: 0x0, + Slot: 0x16, + Function: 0x4, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:16.5", + ClassId: 1540, + Bus: 0x0, + Slot: 0x16, + Function: 0x5, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:16.6", + ClassId: 1540, + Bus: 0x0, + Slot: 0x16, + Function: 0x6, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:16.7", + ClassId: 1540, + Bus: 0x0, + Slot: 0x16, + Function: 0x7, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:17.0", + ClassId: 1540, + Bus: 0x0, + Slot: 0x17, + Function: 0x0, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:17.1", + ClassId: 1540, + Bus: 0x0, + Slot: 0x17, + Function: 0x1, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:17.2", + ClassId: 1540, + Bus: 0x0, + Slot: 0x17, + Function: 0x2, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:17.3", + ClassId: 1540, + Bus: 0x0, + Slot: 0x17, + Function: 0x3, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:17.4", + ClassId: 1540, + Bus: 0x0, + Slot: 0x17, + Function: 0x4, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:17.5", + ClassId: 1540, + Bus: 0x0, + Slot: 0x17, + Function: 0x5, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:17.6", + ClassId: 1540, + Bus: 0x0, + Slot: 0x17, + Function: 0x6, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:17.7", + ClassId: 1540, + Bus: 0x0, + Slot: 0x17, + Function: 0x7, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:18.0", + ClassId: 1540, + Bus: 0x0, + Slot: 0x18, + Function: 0x0, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:18.1", + ClassId: 1540, + Bus: 0x0, + Slot: 0x18, + Function: 0x1, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:18.2", + ClassId: 1540, + Bus: 0x0, + Slot: 0x18, + Function: 0x2, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:18.3", + ClassId: 1540, + Bus: 0x0, + Slot: 0x18, + Function: 0x3, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:18.4", + ClassId: 1540, + Bus: 0x0, + Slot: 0x18, + Function: 0x4, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:18.5", + ClassId: 1540, + Bus: 0x0, + Slot: 0x18, + Function: 0x5, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:18.6", + ClassId: 1540, + Bus: 0x0, + Slot: 0x18, + Function: 0x6, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:00:18.7", + ClassId: 1540, + Bus: 0x0, + Slot: 0x18, + Function: 0x7, + VendorId: 5549, + SubVendorId: 0, + VendorName: "VMware", + DeviceId: 1952, + SubDeviceId: 0, + ParentBridge: "", + DeviceName: "PCI Express Root Port", + }, + { + Id: "0000:03:00.0", + ClassId: 263, + Bus: 0x3, + Slot: 0x0, + Function: 0x0, + VendorId: 5549, + SubVendorId: 5549, + VendorName: "VMware", + DeviceId: 1984, + SubDeviceId: 1984, + ParentBridge: "0000:00:15.0", + DeviceName: "PVSCSI SCSI Controller", + }, + { + Id: "0000:0b:00.0", + ClassId: 512, + Bus: 0xb, + Slot: 0x0, + Function: 0x0, + VendorId: 5549, + SubVendorId: 5549, + VendorName: "VMware Inc.", + DeviceId: 1968, + SubDeviceId: 1968, + ParentBridge: "0000:00:16.0", + DeviceName: "vmxnet3 Virtual Ethernet Controller", + }, + { + Id: "0000:13:00.0", + ClassId: 512, + Bus: 0x13, + Slot: 0x0, + Function: 0x0, + VendorId: 5549, + SubVendorId: 5549, + VendorName: "VMware Inc.", + DeviceId: 1968, + SubDeviceId: 1968, + ParentBridge: "0000:00:17.0", + DeviceName: "vmxnet3 Virtual Ethernet Controller", + }, + }, + CpuFeature: []types.HostCpuIdInfo{ + { + Level: 0, + Vendor: "", + Eax: "0000:0000:0000:0000:0000:0000:0000:1101", + Ebx: "0111:0101:0110:1110:0110:0101:0100:0111", + Ecx: "0110:1100:0110:0101:0111:0100:0110:1110", + Edx: "0100:1001:0110:0101:0110:1110:0110:1001", + }, + { + Level: 1, + Vendor: "", + Eax: "0000:0000:0000:0010:0000:0110:1101:0111", + Ebx: "0000:0000:0000:0001:0000:1000:0000:0000", + Ecx: "1001:0111:1011:1010:0010:0010:0010:1011", + Edx: "0000:1111:1010:1011:1111:1011:1111:1111", + }, + { + Level: -2147483648, + Vendor: "", + Eax: "1000:0000:0000:0000:0000:0000:0000:1000", + Ebx: "0000:0000:0000:0000:0000:0000:0000:0000", + Ecx: "0000:0000:0000:0000:0000:0000:0000:0000", + Edx: "0000:0000:0000:0000:0000:0000:0000:0000", + }, + { + Level: -2147483647, + Vendor: "", + Eax: "0000:0000:0000:0000:0000:0000:0000:0000", + Ebx: "0000:0000:0000:0000:0000:0000:0000:0000", + Ecx: "0000:0000:0000:0000:0000:0000:0000:0001", + Edx: "0010:1000:0001:0000:0000:1000:0000:0000", + }, + { + Level: -2147483640, + Vendor: "", + Eax: "0000:0000:0000:0000:0011:0000:0010:1010", + Ebx: "0000:0000:0000:0000:0000:0000:0000:0000", + Ecx: "0000:0000:0000:0000:0000:0000:0000:0000", + Edx: "0000:0000:0000:0000:0000:0000:0000:0000", + }, + }, + BiosInfo: &types.HostBIOSInfo{ + BiosVersion: "6.00", + ReleaseDate: nil, + Vendor: "", + MajorRelease: 0, + MinorRelease: 0, + FirmwareMajorRelease: 0, + FirmwareMinorRelease: 0, + }, + ReliableMemoryInfo: &types.HostReliableMemoryInfo{}, +} + +func init() { + date, _ := time.Parse("2006-01-02", "2015-07-02") + + HostHardwareInfo.BiosInfo.ReleaseDate = &date +} diff --git a/vendor/github.com/vmware/govmomi/simulator/esx/host_system.go b/vendor/github.com/vmware/govmomi/simulator/esx/host_system.go new file mode 100644 index 00000000..e25f39a5 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/esx/host_system.go @@ -0,0 +1,1788 @@ +/* +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 esx + +import ( + "time" + + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +var HostSystem = mo.HostSystem{ + ManagedEntity: mo.ManagedEntity{ + ExtensibleManagedObject: mo.ExtensibleManagedObject{ + Self: types.ManagedObjectReference{Type: "HostSystem", Value: "ha-host"}, + Value: nil, + AvailableField: nil, + }, + Parent: &types.ManagedObjectReference{Type: "ComputeResource", Value: "ha-compute-res"}, + CustomValue: nil, + OverallStatus: "", + ConfigStatus: "", + ConfigIssue: nil, + EffectiveRole: nil, + Permission: nil, + Name: "", + DisabledMethod: nil, + RecentTask: nil, + DeclaredAlarmState: nil, + TriggeredAlarmState: nil, + AlarmActionsEnabled: (*bool)(nil), + Tag: nil, + }, + Runtime: types.HostRuntimeInfo{ + DynamicData: types.DynamicData{}, + ConnectionState: "connected", + PowerState: "poweredOn", + StandbyMode: "", + InMaintenanceMode: false, + BootTime: (*time.Time)(nil), + HealthSystemRuntime: &types.HealthSystemRuntime{ + DynamicData: types.DynamicData{}, + SystemHealthInfo: &types.HostSystemHealthInfo{ + DynamicData: types.DynamicData{}, + NumericSensorInfo: []types.HostNumericSensorInfo{ + { + DynamicData: types.DynamicData{}, + Name: "VMware Rollup Health State", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "system", + }, + { + DynamicData: types.DynamicData{}, + Name: "CPU socket #0 Level-1 Cache is 16384 B", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Processors", + }, + { + DynamicData: types.DynamicData{}, + Name: "CPU socket #0 Level-2 Cache is 0 B", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Processors", + }, + { + DynamicData: types.DynamicData{}, + Name: "CPU socket #1 Level-1 Cache is 16384 B", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Processors", + }, + { + DynamicData: types.DynamicData{}, + Name: "CPU socket #1 Level-2 Cache is 0 B", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Processors", + }, + { + DynamicData: types.DynamicData{}, + Name: "Phoenix Technologies LTD System BIOS 6.00 2014-05-20 00:00:00.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware, Inc. VMware ESXi 6.0.0 build-3634798 2016-03-07 00:00:00.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware sata-ata-piix 2.12-10vmw.600.2.34.3634798 2016-03-08 07:38:41.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware lsu-lsi-mptsas-plugin 1.0.0-1vmw.600.2.34.3634798 2016-03-08 07:39:28.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-mlx4-core 1.9.7.0-1vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware lsu-lsi-mpt2sas-plugin 1.0.0-4vmw.600.2.34.3634798 2016-03-08 07:39:28.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-aacraid 1.1.5.1-9vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ata-pata-via 0.3.3-2vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-qla4xxx 5.01.03.2-7vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware sata-sata-promise 2.12-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-megaraid-mbox 2.20.5.1-6vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware vsan 6.0.0-2.34.3563498 2016-02-17 17:18:19.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-e1000 8.0.3.1-5vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ata-pata-serverworks 0.4.3-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-mptspi 4.23.01.00-9vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-nx-nic 5.0.621-5vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware block-cciss 3.6.14-10vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-bnx2x 1.78.80.v60.12-1vmw.600.2.34.3634798 2016-03-08 07:38:41.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ipmi-ipmi-devintf 39.1-4vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-mptsas 4.23.01.00-9vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-megaraid2 2.00.4-9vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware nvme 1.0e.0.35-1vmw.600.2.34.3634798 2016-03-08 07:38:46.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware esx-xserver 6.0.0-2.34.3634798 2016-03-08 07:39:27.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware nmlx4-en 3.0.0.0-1vmw.600.2.34.3634798 2016-03-08 07:38:46.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware lsu-hp-hpsa-plugin 1.0.0-1vmw.600.2.34.3634798 2016-03-08 07:39:28.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-megaraid-sas 6.603.55.00-2vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-enic 2.1.2.38-2vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware lsi-msgpt3 06.255.12.00-8vmw.600.2.34.3634798 2016-03-08 07:38:46.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware sata-ahci 3.0-22vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-forcedeth 0.61-2vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ata-pata-atiixp 0.4.6-4vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware elxnet 10.2.309.6v-1vmw.600.2.34.3634798 2016-03-08 07:38:46.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware esx-dvfilter-generic-fastpath 6.0.0-2.34.3634798 2016-03-08 07:39:28.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware uhci-usb-uhci 1.0-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ata-pata-amd 0.3.10-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware sata-sata-sil24 1.1-1vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ohci-usb-ohci 1.0-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-igb 5.0.5.1.1-5vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ata-pata-pdc2027x 1.0-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ehci-ehci-hcd 1.0-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware lsu-lsi-lsi-mr3-plugin 1.0.0-2vmw.600.2.34.3634798 2016-03-08 07:39:28.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-ixgbe 3.7.13.7.14iov-20vmw.600.2.34.3634798 2016-03-08 07:38:41.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware vsanhealth 6.0.0-3000000.3.0.2.34.3544323 2016-02-12 06:45:30.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-cnic 1.78.76.v60.13-2vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware sata-sata-svw 2.3-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ipmi-ipmi-msghandler 39.1-4vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware emulex-esx-elxnetcli 10.2.309.6v-2.34.3634798 2016-03-08 07:39:28.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-aic79xx 3.1-5vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware qlnativefc 2.0.12.0-5vmw.600.2.34.3634798 2016-03-08 07:38:46.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware lsu-lsi-lsi-msgpt3-plugin 1.0.0-1vmw.600.2.34.3634798 2016-03-08 07:39:28.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ima-qla4xxx 2.02.18-1vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-mlx4-en 1.9.7.0-1vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-e1000e 3.2.2.1-1vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-tg3 3.131d.v60.4-2vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-hpsa 6.0.0.44-4vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-bnx2fc 1.78.78.v60.8-1vmw.600.2.34.3634798 2016-03-08 07:38:41.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware cpu-microcode 6.0.0-2.34.3634798 2016-03-08 07:39:28.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-fnic 1.5.0.45-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware nmlx4-rdma 3.0.0.0-1vmw.600.2.34.3634798 2016-03-08 07:38:46.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-vmxnet3 1.1.3.0-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware lpfc 10.2.309.8-2vmw.600.2.34.3634798 2016-03-08 07:38:46.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware esx-ui 1.0.0-3617585 2016-03-03 04:52:43.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ata-pata-cmd64x 0.2.5-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware lsi-mr3 6.605.08.00-7vmw.600.2.34.3634798 2016-03-08 07:38:46.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ata-pata-hpt3x2n 0.3.4-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware sata-sata-nv 3.5-4vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware misc-cnic-register 1.78.75.v60.7-1vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware lsu-lsi-megaraid-sas-plugin 1.0.0-2vmw.600.2.34.3634798 2016-03-08 07:39:28.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ata-pata-sil680 0.4.8-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware esx-tboot 6.0.0-2.34.3634798 2016-03-08 07:39:27.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware xhci-xhci 1.0-3vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-ips 7.12.05-4vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-adp94xx 1.0.8.12-6vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware rste 2.0.2.0088-4vmw.600.2.34.3634798 2016-03-08 07:38:46.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware ipmi-ipmi-si-drv 39.1-4vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMWARE mtip32xx-native 3.8.5-1vmw.600.2.34.3634798 2016-03-08 07:38:46.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-mpt2sas 19.00.00.00-1vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware misc-drivers 6.0.0-2.34.3634798 2016-03-08 07:38:41.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware nmlx4-core 3.0.0.0-1vmw.600.2.34.3634798 2016-03-08 07:38:46.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware sata-sata-sil 2.3-4vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware esx-base 6.0.0-2.34.3634798 2016-03-08 07:39:18.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware scsi-bnx2i 2.78.76.v60.8-1vmw.600.2.34.3634798 2016-03-08 07:38:41.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "VMware net-bnx2 2.2.4f.v60.10-1vmw.600.2.34.3634798 2016-03-08 07:38:45.000", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "e1000 driver 8.0.3.1-NAPI", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + { + DynamicData: types.DynamicData{}, + Name: "e1000 device firmware N/A", + HealthState: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Sensor is operating under normal conditions", + }, + Key: "green", + }, + CurrentReading: 0, + UnitModifier: 0, + BaseUnits: "", + RateUnits: "", + SensorType: "Software Components", + }, + }, + }, + HardwareStatusInfo: &types.HostHardwareStatusInfo{ + DynamicData: types.DynamicData{}, + MemoryStatusInfo: nil, + CpuStatusInfo: []types.BaseHostHardwareElementInfo{ + &types.HostHardwareElementInfo{ + DynamicData: types.DynamicData{}, + Name: "CPU socket #0", + Status: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Physical element is functioning as expected", + }, + Key: "Green", + }, + }, + &types.HostHardwareElementInfo{ + DynamicData: types.DynamicData{}, + Name: "CPU socket #1", + Status: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Green", + Summary: "Physical element is functioning as expected", + }, + Key: "Green", + }, + }, + }, + StorageStatusInfo: nil, + }, + }, + DasHostState: (*types.ClusterDasFdmHostState)(nil), + TpmPcrValues: nil, + VsanRuntimeInfo: &types.VsanHostRuntimeInfo{}, + NetworkRuntimeInfo: &types.HostRuntimeInfoNetworkRuntimeInfo{ + DynamicData: types.DynamicData{}, + NetStackInstanceRuntimeInfo: []types.HostRuntimeInfoNetStackInstanceRuntimeInfo{ + { + DynamicData: types.DynamicData{}, + NetStackInstanceKey: "defaultTcpipStack", + State: "active", + VmknicKeys: []string{"vmk0"}, + MaxNumberOfConnections: 11000, + CurrentIpV6Enabled: types.NewBool(true), + }, + }, + NetworkResourceRuntime: (*types.HostNetworkResourceRuntime)(nil), + }, + VFlashResourceRuntimeInfo: (*types.HostVFlashManagerVFlashResourceRunTimeInfo)(nil), + HostMaxVirtualDiskCapacity: 68169720922112, + }, + Summary: types.HostListSummary{ + DynamicData: types.DynamicData{}, + Host: &types.ManagedObjectReference{Type: "HostSystem", Value: "ha-host"}, + Hardware: &types.HostHardwareSummary{ + DynamicData: types.DynamicData{}, + Vendor: "VMware, Inc.", + Model: "VMware Virtual Platform", + Uuid: "564d2f12-8041-639b-5018-05a835b72eaf", + OtherIdentifyingInfo: []types.HostSystemIdentificationInfo{ + { + DynamicData: types.DynamicData{}, + IdentifierValue: " No Asset Tag", + IdentifierType: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Asset Tag", + Summary: "Asset tag of the system", + }, + Key: "AssetTag", + }, + }, + { + DynamicData: types.DynamicData{}, + IdentifierValue: "[MS_VM_CERT/SHA1/27d66596a61c48dd3dc7216fd715126e33f59ae7]", + IdentifierType: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "OEM specific string", + Summary: "OEM specific string", + }, + Key: "OemSpecificString", + }, + }, + { + DynamicData: types.DynamicData{}, + IdentifierValue: "Welcome to the Virtual Machine", + IdentifierType: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "OEM specific string", + Summary: "OEM specific string", + }, + Key: "OemSpecificString", + }, + }, + { + DynamicData: types.DynamicData{}, + IdentifierValue: "VMware-56 4d 2f 12 80 41 63 9b-50 18 05 a8 35 b7 2e af", + IdentifierType: &types.ElementDescription{ + Description: types.Description{ + DynamicData: types.DynamicData{}, + Label: "Service tag", + Summary: "Service tag of the system", + }, + Key: "ServiceTag", + }, + }, + }, + MemorySize: 4294430720, + CpuModel: "Intel(R) Core(TM) i7-3615QM CPU @ 2.30GHz", + CpuMhz: 2294, + NumCpuPkgs: 2, + NumCpuCores: 2, + NumCpuThreads: 2, + NumNics: 1, + NumHBAs: 3, + }, + Runtime: (*types.HostRuntimeInfo)(nil), + Config: types.HostConfigSummary{ + DynamicData: types.DynamicData{}, + Name: "localhost.localdomain", + Port: 902, + SslThumbprint: "", + Product: &HostConfigInfo.Product, + VmotionEnabled: false, + FaultToleranceEnabled: types.NewBool(true), + FeatureVersion: nil, + AgentVmDatastore: (*types.ManagedObjectReference)(nil), + AgentVmNetwork: (*types.ManagedObjectReference)(nil), + }, + QuickStats: types.HostListSummaryQuickStats{ + DynamicData: types.DynamicData{}, + OverallCpuUsage: 67, + OverallMemoryUsage: 1404, + DistributedCpuFairness: 0, + DistributedMemoryFairness: 0, + Uptime: 77229, + }, + OverallStatus: "gray", + RebootRequired: false, + CustomValue: nil, + ManagementServerIp: "", + MaxEVCModeKey: "", + CurrentEVCModeKey: "", + Gateway: (*types.HostListSummaryGatewaySummary)(nil), + }, + Hardware: (*types.HostHardwareInfo)(nil), + Capability: (*types.HostCapability)(nil), + LicensableResource: types.HostLicensableResourceInfo{}, + ConfigManager: types.HostConfigManager{ + DynamicData: types.DynamicData{}, + CpuScheduler: &types.ManagedObjectReference{Type: "HostCpuSchedulerSystem", Value: "cpuSchedulerSystem"}, + DatastoreSystem: &types.ManagedObjectReference{Type: "HostDatastoreSystem", Value: "ha-datastoresystem"}, + MemoryManager: &types.ManagedObjectReference{Type: "HostMemorySystem", Value: "memoryManagerSystem"}, + StorageSystem: &types.ManagedObjectReference{Type: "HostStorageSystem", Value: "storageSystem"}, + NetworkSystem: &types.ManagedObjectReference{Type: "HostNetworkSystem", Value: "networkSystem"}, + VmotionSystem: &types.ManagedObjectReference{Type: "HostVMotionSystem", Value: "ha-vmotion-system"}, + VirtualNicManager: &types.ManagedObjectReference{Type: "HostVirtualNicManager", Value: "ha-vnic-mgr"}, + ServiceSystem: &types.ManagedObjectReference{Type: "HostServiceSystem", Value: "serviceSystem"}, + FirewallSystem: &types.ManagedObjectReference{Type: "HostFirewallSystem", Value: "firewallSystem"}, + AdvancedOption: &types.ManagedObjectReference{Type: "OptionManager", Value: "ha-adv-options"}, + DiagnosticSystem: &types.ManagedObjectReference{Type: "HostDiagnosticSystem", Value: "diagnosticsystem"}, + AutoStartManager: &types.ManagedObjectReference{Type: "HostAutoStartManager", Value: "ha-autostart-mgr"}, + SnmpSystem: &types.ManagedObjectReference{Type: "HostSnmpSystem", Value: "ha-snmp-agent"}, + DateTimeSystem: &types.ManagedObjectReference{Type: "HostDateTimeSystem", Value: "dateTimeSystem"}, + PatchManager: &types.ManagedObjectReference{Type: "HostPatchManager", Value: "ha-host-patch-manager"}, + ImageConfigManager: &types.ManagedObjectReference{Type: "HostImageConfigManager", Value: "ha-image-config-manager"}, + BootDeviceSystem: (*types.ManagedObjectReference)(nil), + FirmwareSystem: &types.ManagedObjectReference{Type: "HostFirmwareSystem", Value: "ha-firmwareSystem"}, + HealthStatusSystem: &types.ManagedObjectReference{Type: "HostHealthStatusSystem", Value: "healthStatusSystem"}, + PciPassthruSystem: &types.ManagedObjectReference{Type: "HostPciPassthruSystem", Value: "ha-pcipassthrusystem"}, + LicenseManager: &types.ManagedObjectReference{Type: "LicenseManager", Value: "ha-license-manager"}, + KernelModuleSystem: &types.ManagedObjectReference{Type: "HostKernelModuleSystem", Value: "kernelModuleSystem"}, + AuthenticationManager: &types.ManagedObjectReference{Type: "HostAuthenticationManager", Value: "ha-auth-manager"}, + PowerSystem: &types.ManagedObjectReference{Type: "HostPowerSystem", Value: "ha-power-system"}, + CacheConfigurationManager: &types.ManagedObjectReference{Type: "HostCacheConfigurationManager", Value: "ha-cache-configuration-manager"}, + EsxAgentHostManager: (*types.ManagedObjectReference)(nil), + IscsiManager: &types.ManagedObjectReference{Type: "IscsiManager", Value: "iscsiManager"}, + VFlashManager: &types.ManagedObjectReference{Type: "HostVFlashManager", Value: "ha-vflash-manager"}, + VsanSystem: &types.ManagedObjectReference{Type: "HostVsanSystem", Value: "vsanSystem"}, + MessageBusProxy: &types.ManagedObjectReference{Type: "MessageBusProxy", Value: "messageBusProxy"}, + UserDirectory: &types.ManagedObjectReference{Type: "UserDirectory", Value: "ha-user-directory"}, + AccountManager: &types.ManagedObjectReference{Type: "HostLocalAccountManager", Value: "ha-localacctmgr"}, + HostAccessManager: &types.ManagedObjectReference{Type: "HostAccessManager", Value: "ha-host-access-manager"}, + GraphicsManager: &types.ManagedObjectReference{Type: "HostGraphicsManager", Value: "ha-graphics-manager"}, + VsanInternalSystem: &types.ManagedObjectReference{Type: "HostVsanInternalSystem", Value: "ha-vsan-internal-system"}, + CertificateManager: &types.ManagedObjectReference{Type: "HostCertificateManager", Value: "ha-certificate-manager"}, + }, + Config: &HostConfigInfo, + Vm: nil, + Datastore: nil, + Network: nil, + DatastoreBrowser: types.ManagedObjectReference{Type: "HostDatastoreBrowser", Value: "ha-host-datastorebrowser"}, + SystemResources: (*types.HostSystemResourceInfo)(nil), +} diff --git a/vendor/github.com/vmware/govmomi/simulator/esx/resource_pool.go b/vendor/github.com/vmware/govmomi/simulator/esx/resource_pool.go new file mode 100644 index 00000000..08d6da5d --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/esx/resource_pool.go @@ -0,0 +1,162 @@ +/* +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 esx + +import ( + "time" + + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +var ResourcePool = mo.ResourcePool{ + ManagedEntity: mo.ManagedEntity{ + ExtensibleManagedObject: mo.ExtensibleManagedObject{ + Self: types.ManagedObjectReference{Type: "ResourcePool", Value: "ha-root-pool"}, + Value: nil, + AvailableField: nil, + }, + Parent: &types.ManagedObjectReference{Type: "ComputeResource", Value: "ha-compute-res"}, + CustomValue: nil, + OverallStatus: "green", + ConfigStatus: "green", + ConfigIssue: nil, + EffectiveRole: []int32{-1}, + Permission: nil, + Name: "Resources", + DisabledMethod: []string{"CreateVApp", "CreateChildVM_Task"}, + RecentTask: nil, + DeclaredAlarmState: nil, + TriggeredAlarmState: nil, + AlarmActionsEnabled: (*bool)(nil), + Tag: nil, + }, + Summary: &types.ResourcePoolSummary{ + DynamicData: types.DynamicData{}, + Name: "Resources", + Config: types.ResourceConfigSpec{ + DynamicData: types.DynamicData{}, + Entity: &types.ManagedObjectReference{Type: "ResourcePool", Value: "ha-root-pool"}, + ChangeVersion: "", + LastModified: (*time.Time)(nil), + CpuAllocation: &types.ResourceAllocationInfo{ + DynamicData: types.DynamicData{}, + Reservation: 4121, + ExpandableReservation: types.NewBool(false), + Limit: 4121, + Shares: &types.SharesInfo{ + DynamicData: types.DynamicData{}, + Shares: 9000, + Level: "custom", + }, + OverheadLimit: 0, + }, + MemoryAllocation: &types.ResourceAllocationInfo{ + DynamicData: types.DynamicData{}, + Reservation: 961, + ExpandableReservation: types.NewBool(false), + Limit: 961, + Shares: &types.SharesInfo{ + DynamicData: types.DynamicData{}, + Shares: 9000, + Level: "custom", + }, + OverheadLimit: 0, + }, + }, + Runtime: types.ResourcePoolRuntimeInfo{ + DynamicData: types.DynamicData{}, + Memory: types.ResourcePoolResourceUsage{ + DynamicData: types.DynamicData{}, + ReservationUsed: 0, + ReservationUsedForVm: 0, + UnreservedForPool: 1007681536, + UnreservedForVm: 1007681536, + OverallUsage: 0, + MaxUsage: 1007681536, + }, + Cpu: types.ResourcePoolResourceUsage{ + DynamicData: types.DynamicData{}, + ReservationUsed: 0, + ReservationUsedForVm: 0, + UnreservedForPool: 4121, + UnreservedForVm: 4121, + OverallUsage: 0, + MaxUsage: 4121, + }, + OverallStatus: "green", + }, + QuickStats: (*types.ResourcePoolQuickStats)(nil), + ConfiguredMemoryMB: 0, + }, + Runtime: types.ResourcePoolRuntimeInfo{ + DynamicData: types.DynamicData{}, + Memory: types.ResourcePoolResourceUsage{ + DynamicData: types.DynamicData{}, + ReservationUsed: 0, + ReservationUsedForVm: 0, + UnreservedForPool: 1007681536, + UnreservedForVm: 1007681536, + OverallUsage: 0, + MaxUsage: 1007681536, + }, + Cpu: types.ResourcePoolResourceUsage{ + DynamicData: types.DynamicData{}, + ReservationUsed: 0, + ReservationUsedForVm: 0, + UnreservedForPool: 4121, + UnreservedForVm: 4121, + OverallUsage: 0, + MaxUsage: 4121, + }, + OverallStatus: "green", + }, + Owner: types.ManagedObjectReference{Type: "ComputeResource", Value: "ha-compute-res"}, + ResourcePool: nil, + Vm: nil, + Config: types.ResourceConfigSpec{ + DynamicData: types.DynamicData{}, + Entity: &types.ManagedObjectReference{Type: "ResourcePool", Value: "ha-root-pool"}, + ChangeVersion: "", + LastModified: (*time.Time)(nil), + CpuAllocation: &types.ResourceAllocationInfo{ + DynamicData: types.DynamicData{}, + Reservation: 4121, + ExpandableReservation: types.NewBool(false), + Limit: 4121, + Shares: &types.SharesInfo{ + DynamicData: types.DynamicData{}, + Shares: 9000, + Level: "custom", + }, + OverheadLimit: 0, + }, + MemoryAllocation: &types.ResourceAllocationInfo{ + DynamicData: types.DynamicData{}, + Reservation: 961, + ExpandableReservation: types.NewBool(false), + Limit: 961, + Shares: &types.SharesInfo{ + DynamicData: types.DynamicData{}, + Shares: 9000, + Level: "custom", + }, + OverheadLimit: 0, + }, + }, + ChildConfiguration: nil, +} diff --git a/vendor/github.com/vmware/govmomi/simulator/esx/root_folder.go b/vendor/github.com/vmware/govmomi/simulator/esx/root_folder.go new file mode 100644 index 00000000..bb40706f --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/esx/root_folder.go @@ -0,0 +1,73 @@ +/* +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 esx + +import ( + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +var RootFolder = mo.Folder{ + ManagedEntity: mo.ManagedEntity{ + ExtensibleManagedObject: mo.ExtensibleManagedObject{ + Self: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"}, + Value: nil, + AvailableField: nil, + }, + Parent: (*types.ManagedObjectReference)(nil), + CustomValue: nil, + OverallStatus: "green", + ConfigStatus: "green", + ConfigIssue: nil, + EffectiveRole: []int32{-1}, + Permission: []types.Permission{ + { + DynamicData: types.DynamicData{}, + Entity: &types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"}, + Principal: "vpxuser", + Group: false, + RoleId: -1, + Propagate: true, + }, + { + DynamicData: types.DynamicData{}, + Entity: &types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"}, + Principal: "dcui", + Group: false, + RoleId: -1, + Propagate: true, + }, + { + DynamicData: types.DynamicData{}, + Entity: &types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"}, + Principal: "root", + Group: false, + RoleId: -1, + Propagate: true, + }, + }, + Name: "ha-folder-root", + DisabledMethod: nil, + RecentTask: nil, + DeclaredAlarmState: nil, + TriggeredAlarmState: nil, + AlarmActionsEnabled: (*bool)(nil), + Tag: nil, + }, + ChildType: []string{"Datacenter"}, + ChildEntity: nil, +} diff --git a/vendor/github.com/vmware/govmomi/simulator/esx/service_content.go b/vendor/github.com/vmware/govmomi/simulator/esx/service_content.go new file mode 100644 index 00000000..88f2cbbb --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/esx/service_content.go @@ -0,0 +1,79 @@ +/* +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 esx + +import "github.com/vmware/govmomi/vim25/types" + +var ServiceContent = types.ServiceContent{ + DynamicData: types.DynamicData{}, + RootFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"}, + PropertyCollector: types.ManagedObjectReference{Type: "PropertyCollector", Value: "ha-property-collector"}, + ViewManager: &types.ManagedObjectReference{Type: "ViewManager", Value: "ViewManager"}, + About: types.AboutInfo{ + DynamicData: types.DynamicData{}, + Name: "VMware ESXi", + FullName: "VMware ESXi 6.0.0 build-3620759", + Vendor: "VMware, Inc.", + Version: "6.0.0", + Build: "3620759", + LocaleVersion: "INTL", + LocaleBuild: "000", + OsType: "vmnix-x86", + ProductLineId: "embeddedEsx", + ApiType: "HostAgent", + ApiVersion: "6.0", + InstanceUuid: "", + LicenseProductName: "VMware ESX Server", + LicenseProductVersion: "6.0", + }, + Setting: &types.ManagedObjectReference{Type: "OptionManager", Value: "HostAgentSettings"}, + UserDirectory: &types.ManagedObjectReference{Type: "UserDirectory", Value: "ha-user-directory"}, + SessionManager: &types.ManagedObjectReference{Type: "SessionManager", Value: "ha-sessionmgr"}, + AuthorizationManager: &types.ManagedObjectReference{Type: "AuthorizationManager", Value: "ha-authmgr"}, + ServiceManager: &types.ManagedObjectReference{Type: "ServiceManager", Value: "ha-servicemanager"}, + PerfManager: &types.ManagedObjectReference{Type: "PerformanceManager", Value: "ha-perfmgr"}, + ScheduledTaskManager: (*types.ManagedObjectReference)(nil), + AlarmManager: (*types.ManagedObjectReference)(nil), + EventManager: &types.ManagedObjectReference{Type: "EventManager", Value: "ha-eventmgr"}, + TaskManager: &types.ManagedObjectReference{Type: "TaskManager", Value: "ha-taskmgr"}, + ExtensionManager: (*types.ManagedObjectReference)(nil), + CustomizationSpecManager: (*types.ManagedObjectReference)(nil), + CustomFieldsManager: (*types.ManagedObjectReference)(nil), + AccountManager: &types.ManagedObjectReference{Type: "HostLocalAccountManager", Value: "ha-localacctmgr"}, + DiagnosticManager: &types.ManagedObjectReference{Type: "DiagnosticManager", Value: "ha-diagnosticmgr"}, + LicenseManager: &types.ManagedObjectReference{Type: "LicenseManager", Value: "ha-license-manager"}, + SearchIndex: &types.ManagedObjectReference{Type: "SearchIndex", Value: "ha-searchindex"}, + FileManager: &types.ManagedObjectReference{Type: "FileManager", Value: "ha-nfc-file-manager"}, + DatastoreNamespaceManager: &types.ManagedObjectReference{Type: "DatastoreNamespaceManager", Value: "ha-datastore-namespace-manager"}, + VirtualDiskManager: &types.ManagedObjectReference{Type: "VirtualDiskManager", Value: "ha-vdiskmanager"}, + VirtualizationManager: (*types.ManagedObjectReference)(nil), + SnmpSystem: (*types.ManagedObjectReference)(nil), + VmProvisioningChecker: (*types.ManagedObjectReference)(nil), + VmCompatibilityChecker: (*types.ManagedObjectReference)(nil), + OvfManager: &types.ManagedObjectReference{Type: "OvfManager", Value: "ha-ovf-manager"}, + IpPoolManager: (*types.ManagedObjectReference)(nil), + DvSwitchManager: &types.ManagedObjectReference{Type: "DistributedVirtualSwitchManager", Value: "ha-dvsmanager"}, + HostProfileManager: (*types.ManagedObjectReference)(nil), + ClusterProfileManager: (*types.ManagedObjectReference)(nil), + ComplianceManager: (*types.ManagedObjectReference)(nil), + LocalizationManager: &types.ManagedObjectReference{Type: "LocalizationManager", Value: "ha-l10n-manager"}, + StorageResourceManager: &types.ManagedObjectReference{Type: "StorageResourceManager", Value: "ha-storage-resource-manager"}, + GuestOperationsManager: &types.ManagedObjectReference{Type: "GuestOperationsManager", Value: "ha-guest-operations-manager"}, + OverheadMemoryManager: (*types.ManagedObjectReference)(nil), + CertificateManager: (*types.ManagedObjectReference)(nil), + IoFilterManager: (*types.ManagedObjectReference)(nil), +} diff --git a/vendor/github.com/vmware/govmomi/simulator/esx/setting.go b/vendor/github.com/vmware/govmomi/simulator/esx/setting.go new file mode 100644 index 00000000..c5c6ae60 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/esx/setting.go @@ -0,0 +1,28 @@ +/* +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 esx + +import "github.com/vmware/govmomi/vim25/types" + +// Setting is captured from ESX's HostSystem.configManager.advancedOption +var Setting = []types.BaseOptionValue{ + // This list is currently pruned to include a single option for testing + &types.OptionValue{ + Key: "Config.HostAgent.log.level", + Value: "info", + }, +} diff --git a/vendor/github.com/vmware/govmomi/simulator/esx/virtual_device.go b/vendor/github.com/vmware/govmomi/simulator/esx/virtual_device.go new file mode 100644 index 00000000..0d394364 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/esx/virtual_device.go @@ -0,0 +1,243 @@ +/* +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 esx + +import "github.com/vmware/govmomi/vim25/types" + +// VirtualDevice is the default set of VirtualDevice types created for a VirtualMachine +var VirtualDevice = []types.BaseVirtualDevice{ + &types.VirtualIDEController{ + VirtualController: types.VirtualController{ + VirtualDevice: types.VirtualDevice{ + DynamicData: types.DynamicData{}, + Key: 200, + DeviceInfo: &types.Description{ + DynamicData: types.DynamicData{}, + Label: "IDE 0", + Summary: "IDE 0", + }, + Backing: nil, + Connectable: (*types.VirtualDeviceConnectInfo)(nil), + SlotInfo: nil, + ControllerKey: 0, + UnitNumber: (*int32)(nil), + }, + BusNumber: 0, + Device: nil, + }, + }, + &types.VirtualIDEController{ + VirtualController: types.VirtualController{ + VirtualDevice: types.VirtualDevice{ + DynamicData: types.DynamicData{}, + Key: 201, + DeviceInfo: &types.Description{ + DynamicData: types.DynamicData{}, + Label: "IDE 1", + Summary: "IDE 1", + }, + Backing: nil, + Connectable: (*types.VirtualDeviceConnectInfo)(nil), + SlotInfo: nil, + ControllerKey: 0, + UnitNumber: (*int32)(nil), + }, + BusNumber: 1, + Device: nil, + }, + }, + &types.VirtualPS2Controller{ + VirtualController: types.VirtualController{ + VirtualDevice: types.VirtualDevice{ + DynamicData: types.DynamicData{}, + Key: 300, + DeviceInfo: &types.Description{ + DynamicData: types.DynamicData{}, + Label: "PS2 controller 0", + Summary: "PS2 controller 0", + }, + Backing: nil, + Connectable: (*types.VirtualDeviceConnectInfo)(nil), + SlotInfo: nil, + ControllerKey: 0, + UnitNumber: (*int32)(nil), + }, + BusNumber: 0, + Device: []int32{600, 700}, + }, + }, + &types.VirtualPCIController{ + VirtualController: types.VirtualController{ + VirtualDevice: types.VirtualDevice{ + DynamicData: types.DynamicData{}, + Key: 100, + DeviceInfo: &types.Description{ + DynamicData: types.DynamicData{}, + Label: "PCI controller 0", + Summary: "PCI controller 0", + }, + Backing: nil, + Connectable: (*types.VirtualDeviceConnectInfo)(nil), + SlotInfo: nil, + ControllerKey: 0, + UnitNumber: (*int32)(nil), + }, + BusNumber: 0, + Device: []int32{500, 12000}, + }, + }, + &types.VirtualSIOController{ + VirtualController: types.VirtualController{ + VirtualDevice: types.VirtualDevice{ + DynamicData: types.DynamicData{}, + Key: 400, + DeviceInfo: &types.Description{ + DynamicData: types.DynamicData{}, + Label: "SIO controller 0", + Summary: "SIO controller 0", + }, + Backing: nil, + Connectable: (*types.VirtualDeviceConnectInfo)(nil), + SlotInfo: nil, + ControllerKey: 0, + UnitNumber: (*int32)(nil), + }, + BusNumber: 0, + Device: nil, + }, + }, + &types.VirtualKeyboard{ + VirtualDevice: types.VirtualDevice{ + DynamicData: types.DynamicData{}, + Key: 600, + DeviceInfo: &types.Description{ + DynamicData: types.DynamicData{}, + Label: "Keyboard ", + Summary: "Keyboard", + }, + Backing: nil, + Connectable: (*types.VirtualDeviceConnectInfo)(nil), + SlotInfo: nil, + ControllerKey: 300, + UnitNumber: newInt32(0), + }, + }, + &types.VirtualPointingDevice{ + VirtualDevice: types.VirtualDevice{ + DynamicData: types.DynamicData{}, + Key: 700, + DeviceInfo: &types.Description{ + DynamicData: types.DynamicData{}, + Label: "Pointing device", + Summary: "Pointing device; Device", + }, + Backing: &types.VirtualPointingDeviceDeviceBackingInfo{ + VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{ + VirtualDeviceBackingInfo: types.VirtualDeviceBackingInfo{}, + DeviceName: "", + UseAutoDetect: types.NewBool(false), + }, + HostPointingDevice: "autodetect", + }, + Connectable: (*types.VirtualDeviceConnectInfo)(nil), + SlotInfo: nil, + ControllerKey: 300, + UnitNumber: newInt32(1), + }, + }, + &types.VirtualMachineVideoCard{ + VirtualDevice: types.VirtualDevice{ + DynamicData: types.DynamicData{}, + Key: 500, + DeviceInfo: &types.Description{ + DynamicData: types.DynamicData{}, + Label: "Video card ", + Summary: "Video card", + }, + Backing: nil, + Connectable: (*types.VirtualDeviceConnectInfo)(nil), + SlotInfo: nil, + ControllerKey: 100, + UnitNumber: newInt32(0), + }, + VideoRamSizeInKB: 4096, + NumDisplays: 1, + UseAutoDetect: types.NewBool(false), + Enable3DSupport: types.NewBool(false), + Use3dRenderer: "automatic", + GraphicsMemorySizeInKB: 262144, + }, + &types.VirtualMachineVMCIDevice{ + VirtualDevice: types.VirtualDevice{ + DynamicData: types.DynamicData{}, + Key: 12000, + DeviceInfo: &types.Description{ + DynamicData: types.DynamicData{}, + Label: "VMCI device", + Summary: "Device on the virtual machine PCI bus that provides support for the virtual machine communication interface", + }, + Backing: nil, + Connectable: (*types.VirtualDeviceConnectInfo)(nil), + SlotInfo: nil, + ControllerKey: 100, + UnitNumber: newInt32(17), + }, + Id: -1, + AllowUnrestrictedCommunication: types.NewBool(false), + FilterEnable: types.NewBool(true), + FilterInfo: (*types.VirtualMachineVMCIDeviceFilterInfo)(nil), + }, +} + +// EthernetCard template for types.VirtualEthernetCard +var EthernetCard = types.VirtualE1000{ + VirtualEthernetCard: types.VirtualEthernetCard{ + VirtualDevice: types.VirtualDevice{ + DynamicData: types.DynamicData{}, + Key: 4000, + Backing: &types.VirtualEthernetCardNetworkBackingInfo{ + VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{ + VirtualDeviceBackingInfo: types.VirtualDeviceBackingInfo{}, + DeviceName: "VM Network", + UseAutoDetect: types.NewBool(false), + }, + Network: (*types.ManagedObjectReference)(nil), + InPassthroughMode: types.NewBool(false), + }, + Connectable: &types.VirtualDeviceConnectInfo{ + DynamicData: types.DynamicData{}, + StartConnected: true, + AllowGuestControl: true, + Connected: false, + Status: "untried", + }, + SlotInfo: &types.VirtualDevicePciBusSlotInfo{ + VirtualDeviceBusSlotInfo: types.VirtualDeviceBusSlotInfo{}, + PciSlotNumber: 32, + }, + ControllerKey: 100, + UnitNumber: newInt32(7), + }, + AddressType: "generated", + MacAddress: "", + WakeOnLanEnabled: types.NewBool(true), + }, +} + +func newInt32(n int32) *int32 { + return &n +} diff --git a/vendor/github.com/vmware/govmomi/simulator/file_manager.go b/vendor/github.com/vmware/govmomi/simulator/file_manager.go new file mode 100644 index 00000000..26bb4a25 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/file_manager.go @@ -0,0 +1,257 @@ +/* +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 simulator + +import ( + "io" + "os" + "path" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/simulator/esx" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type FileManager struct { + mo.FileManager +} + +func NewFileManager(ref types.ManagedObjectReference) object.Reference { + m := &FileManager{} + m.Self = ref + return m +} + +func (f *FileManager) findDatastore(ref mo.Reference, name string) (*Datastore, types.BaseMethodFault) { + var refs []types.ManagedObjectReference + + switch obj := ref.(type) { + case *Folder: + refs = obj.ChildEntity + case *StoragePod: + refs = obj.ChildEntity + } + + for _, ref := range refs { + switch obj := Map.Get(ref).(type) { + case *Datastore: + if obj.Name == name { + return obj, nil + } + case *Folder, *StoragePod: + ds, _ := f.findDatastore(obj, name) + if ds != nil { + return ds, nil + } + } + } + + return nil, &types.InvalidDatastore{Name: name} +} + +func (f *FileManager) resolve(dc *types.ManagedObjectReference, name string) (string, types.BaseMethodFault) { + p, fault := parseDatastorePath(name) + if fault != nil { + return "", fault + } + + if dc == nil { + if Map.IsESX() { + dc = &esx.Datacenter.Self + } else { + return "", &types.InvalidArgument{InvalidProperty: "dc"} + } + } + + folder := Map.Get(*dc).(*mo.Datacenter).DatastoreFolder + + ds, fault := f.findDatastore(Map.Get(folder), p.Datastore) + if fault != nil { + return "", fault + } + + dir := ds.Info.GetDatastoreInfo().Url + + return path.Join(dir, p.Path), nil +} + +func (f *FileManager) fault(name string, err error, fault types.BaseFileFault) types.BaseMethodFault { + switch { + case os.IsNotExist(err): + fault = new(types.FileNotFound) + case os.IsExist(err): + fault = new(types.FileAlreadyExists) + } + + fault.GetFileFault().File = name + + return fault.(types.BaseMethodFault) +} + +func (f *FileManager) deleteDatastoreFile(req *types.DeleteDatastoreFile_Task) types.BaseMethodFault { + file, fault := f.resolve(req.Datacenter, req.Name) + if fault != nil { + return fault + } + + _, err := os.Stat(file) + if err != nil { + if os.IsNotExist(err) { + return f.fault(file, err, new(types.CannotDeleteFile)) + } + } + + err = os.RemoveAll(file) + if err != nil { + return f.fault(file, err, new(types.CannotDeleteFile)) + } + + return nil +} + +func (f *FileManager) DeleteDatastoreFileTask(req *types.DeleteDatastoreFile_Task) soap.HasFault { + task := CreateTask(f, "deleteDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) { + return nil, f.deleteDatastoreFile(req) + }) + + task.Run() + + return &methods.DeleteDatastoreFile_TaskBody{ + Res: &types.DeleteDatastoreFile_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (f *FileManager) MakeDirectory(req *types.MakeDirectory) soap.HasFault { + body := &methods.MakeDirectoryBody{} + + name, fault := f.resolve(req.Datacenter, req.Name) + if fault != nil { + body.Fault_ = Fault("", fault) + return body + } + + mkdir := os.Mkdir + + if isTrue(req.CreateParentDirectories) { + mkdir = os.MkdirAll + } + + err := mkdir(name, 0700) + if err != nil { + fault = f.fault(req.Name, err, new(types.CannotCreateFile)) + body.Fault_ = Fault(err.Error(), fault) + return body + } + + return body +} + +func (f *FileManager) moveDatastoreFile(req *types.MoveDatastoreFile_Task) types.BaseMethodFault { + src, fault := f.resolve(req.SourceDatacenter, req.SourceName) + if fault != nil { + return fault + } + + dst, fault := f.resolve(req.DestinationDatacenter, req.DestinationName) + if fault != nil { + return fault + } + + if !isTrue(req.Force) { + _, err := os.Stat(dst) + if err == nil { + return f.fault(dst, nil, new(types.FileAlreadyExistsFault)) + } + } + + err := os.Rename(src, dst) + if err != nil { + return f.fault(src, err, new(types.CannotAccessFile)) + } + + return nil +} + +func (f *FileManager) MoveDatastoreFileTask(req *types.MoveDatastoreFile_Task) soap.HasFault { + task := CreateTask(f, "moveDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) { + return nil, f.moveDatastoreFile(req) + }) + + task.Run() + + return &methods.MoveDatastoreFile_TaskBody{ + Res: &types.MoveDatastoreFile_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (f *FileManager) copyDatastoreFile(req *types.CopyDatastoreFile_Task) types.BaseMethodFault { + src, fault := f.resolve(req.SourceDatacenter, req.SourceName) + if fault != nil { + return fault + } + + dst, fault := f.resolve(req.DestinationDatacenter, req.DestinationName) + if fault != nil { + return fault + } + + if !isTrue(req.Force) { + _, err := os.Stat(dst) + if err == nil { + return f.fault(dst, nil, new(types.FileAlreadyExistsFault)) + } + } + + r, err := os.Open(src) + if err != nil { + return f.fault(dst, err, new(types.CannotAccessFile)) + } + defer r.Close() + + w, err := os.Create(dst) + if err != nil { + return f.fault(dst, err, new(types.CannotCreateFile)) + } + defer w.Close() + + if _, err = io.Copy(w, r); err != nil { + return f.fault(dst, err, new(types.CannotCreateFile)) + } + + return nil +} + +func (f *FileManager) CopyDatastoreFileTask(req *types.CopyDatastoreFile_Task) soap.HasFault { + task := CreateTask(f, "copyDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) { + return nil, f.copyDatastoreFile(req) + }) + + task.Run() + + return &methods.CopyDatastoreFile_TaskBody{ + Res: &types.CopyDatastoreFile_TaskResponse{ + Returnval: task.Self, + }, + } +} diff --git a/vendor/github.com/vmware/govmomi/simulator/folder.go b/vendor/github.com/vmware/govmomi/simulator/folder.go new file mode 100644 index 00000000..2fe93ae0 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/folder.go @@ -0,0 +1,438 @@ +/* +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 simulator + +import ( + "fmt" + "math/rand" + "path" + "sync" + + "github.com/google/uuid" + + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type Folder struct { + mo.Folder + + m sync.Mutex +} + +// update references when objects are added/removed from a Folder +func (f *Folder) update(o mo.Reference, u func(types.ManagedObjectReference, []types.ManagedObjectReference) []types.ManagedObjectReference) { + ref := o.Reference() + + if f.Parent == nil { + return // this is the root folder + } + + switch ref.Type { + case "Datacenter", "Folder": + return // nothing to update + } + + dc := Map.getEntityDatacenter(f) + + switch ref.Type { + case "Network", "DistributedVirtualSwitch", "DistributedVirtualPortgroup": + dc.Network = u(ref, dc.Network) + case "Datastore": + dc.Datastore = u(ref, dc.Datastore) + } +} + +func (f *Folder) putChild(o mo.Entity) { + Map.PutEntity(f, o) + + f.m.Lock() + defer f.m.Unlock() + + f.ChildEntity = AddReference(o.Reference(), f.ChildEntity) + + f.update(o, AddReference) +} + +func (f *Folder) removeChild(o mo.Reference) { + Map.Remove(o.Reference()) + + f.m.Lock() + defer f.m.Unlock() + + f.ChildEntity = RemoveReference(o.Reference(), f.ChildEntity) + + f.update(o, RemoveReference) +} + +func (f *Folder) hasChildType(kind string) bool { + for _, t := range f.ChildType { + if t == kind { + return true + } + } + return false +} + +func (f *Folder) typeNotSupported() *soap.Fault { + return Fault(fmt.Sprintf("%s supports types: %#v", f.Self, f.ChildType), &types.NotSupported{}) +} + +type addStandaloneHost struct { + *Folder + + req *types.AddStandaloneHost_Task +} + +func (add *addStandaloneHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) { + host, err := CreateStandaloneHost(add.Folder, add.req.Spec) + if err != nil { + return nil, err + } + + if add.req.AddConnected { + host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected + } + + return host.Reference(), nil +} + +func (f *Folder) AddStandaloneHostTask(a *types.AddStandaloneHost_Task) soap.HasFault { + r := &methods.AddStandaloneHost_TaskBody{} + + if f.hasChildType("ComputeResource") && f.hasChildType("Folder") { + task := NewTask(&addStandaloneHost{f, a}) + + r.Res = &types.AddStandaloneHost_TaskResponse{ + Returnval: task.Self, + } + + task.Run() + } else { + r.Fault_ = f.typeNotSupported() + } + + return r +} + +func (f *Folder) CreateFolder(c *types.CreateFolder) soap.HasFault { + r := &methods.CreateFolderBody{} + + if f.hasChildType("Folder") { + folder := &Folder{} + + folder.Name = c.Name + folder.ChildType = f.ChildType + + f.putChild(folder) + + r.Res = &types.CreateFolderResponse{ + Returnval: folder.Self, + } + } else { + r.Fault_ = f.typeNotSupported() + } + + return r +} + +// StoragePod aka "Datastore Cluster" +type StoragePod struct { + mo.StoragePod +} + +func (f *Folder) CreateStoragePod(c *types.CreateStoragePod) soap.HasFault { + r := &methods.CreateStoragePodBody{} + + if f.hasChildType("StoragePod") { + pod := &StoragePod{} + + pod.Name = c.Name + pod.ChildType = []string{"Datastore"} + + f.putChild(pod) + + r.Res = &types.CreateStoragePodResponse{ + Returnval: pod.Self, + } + } else { + r.Fault_ = f.typeNotSupported() + } + + return r +} + +func (p *StoragePod) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFault { + return (&Folder{Folder: p.Folder}).MoveIntoFolderTask(c) +} + +func (f *Folder) CreateDatacenter(c *types.CreateDatacenter) soap.HasFault { + r := &methods.CreateDatacenterBody{} + + if f.hasChildType("Datacenter") && f.hasChildType("Folder") { + dc := &mo.Datacenter{} + + dc.Name = c.Name + + f.putChild(dc) + + createDatacenterFolders(dc, true) + + r.Res = &types.CreateDatacenterResponse{ + Returnval: dc.Self, + } + } else { + r.Fault_ = f.typeNotSupported() + } + + return r +} + +func (f *Folder) CreateClusterEx(c *types.CreateClusterEx) soap.HasFault { + r := &methods.CreateClusterExBody{} + + if f.hasChildType("ComputeResource") && f.hasChildType("Folder") { + cluster, err := CreateClusterComputeResource(f, c.Name, c.Spec) + if err != nil { + r.Fault_ = Fault("", err) + return r + } + + r.Res = &types.CreateClusterExResponse{ + Returnval: cluster.Self, + } + } else { + r.Fault_ = f.typeNotSupported() + } + + return r +} + +type createVM struct { + *Folder + + req *types.CreateVM_Task + + register bool +} + +func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) { + vm, err := NewVirtualMachine(c.Folder.Self, &c.req.Config) + if err != nil { + return nil, err + } + + vm.ResourcePool = &c.req.Pool + + if c.req.Host == nil { + var hosts []types.ManagedObjectReference + + pool := Map.Get(c.req.Pool).(mo.Entity) + + switch cr := Map.getEntityComputeResource(pool).(type) { + case *mo.ComputeResource: + hosts = cr.Host + case *ClusterComputeResource: + hosts = cr.Host + } + + // Assuming for now that all hosts have access to the datastore + host := hosts[rand.Intn(len(hosts))] + vm.Runtime.Host = &host + } else { + vm.Runtime.Host = c.req.Host + } + + vm.Summary.Config.VmPathName = vm.Config.Files.VmPathName + vm.Summary.Runtime.Host = vm.Runtime.Host + + err = vm.create(&c.req.Config, c.register) + if err != nil { + return nil, err + } + + c.Folder.putChild(vm) + + host := Map.Get(*vm.Runtime.Host).(*HostSystem) + host.Vm = append(host.Vm, vm.Self) + + for i := range vm.Datastore { + ds := Map.Get(vm.Datastore[i]).(*Datastore) + ds.Vm = append(ds.Vm, vm.Self) + } + + switch rp := Map.Get(*vm.ResourcePool).(type) { + case *ResourcePool: + rp.Vm = append(rp.Vm, vm.Self) + case *VirtualApp: + rp.Vm = append(rp.Vm, vm.Self) + } + + return vm.Reference(), nil +} + +func (f *Folder) CreateVMTask(c *types.CreateVM_Task) soap.HasFault { + r := &methods.CreateVM_TaskBody{} + + task := NewTask(&createVM{f, c, false}) + + r.Res = &types.CreateVM_TaskResponse{ + Returnval: task.Self, + } + + task.Run() + + return r +} + +type registerVM struct { + *Folder + + req *types.RegisterVM_Task +} + +func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) { + if c.req.AsTemplate { + return nil, &types.NotSupported{} + } + + if c.req.Pool == nil { + return nil, &types.InvalidArgument{InvalidProperty: "pool"} + } + + if c.req.Path == "" { + return nil, &types.InvalidArgument{InvalidProperty: "path"} + } + + p := Map.Get(*c.req.Pool).(mo.Entity) + s := Map.SearchIndex() + r := s.FindByDatastorePath(&types.FindByDatastorePath{ + This: s.Reference(), + Path: c.req.Path, + Datacenter: Map.getEntityDatacenter(p).Reference(), + }) + + if ref := r.(*methods.FindByDatastorePathBody).Res.Returnval; ref != nil { + return nil, &types.AlreadyExists{Name: ref.Value} + } + + if c.req.Name == "" { + p, err := parseDatastorePath(c.req.Path) + if err != nil { + return nil, err + } + + c.req.Name = path.Dir(p.Path) + } + + create := NewTask(&createVM{ + Folder: c.Folder, + register: true, + req: &types.CreateVM_Task{ + This: c.Folder.Reference(), + Config: types.VirtualMachineConfigSpec{ + Name: c.req.Name, + Files: &types.VirtualMachineFileInfo{ + VmPathName: c.req.Path, + }, + }, + Pool: *c.req.Pool, + Host: c.req.Host, + }, + }) + + create.Run() + + if create.Info.Error != nil { + return nil, create.Info.Error.Fault + } + + return create.Info.Result, nil +} + +func (f *Folder) RegisterVMTask(c *types.RegisterVM_Task) soap.HasFault { + r := &methods.RegisterVM_TaskBody{} + + task := NewTask(®isterVM{f, c}) + + r.Res = &types.RegisterVM_TaskResponse{ + Returnval: task.Self, + } + + task.Run() + + return r +} + +func (f *Folder) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFault { + task := CreateTask(f, "moveIntoFolder", func(t *Task) (types.AnyType, types.BaseMethodFault) { + for _, ref := range c.List { + obj := Map.Get(ref).(mo.Entity) + + parent, ok := Map.Get(*(obj.Entity()).Parent).(*Folder) + + if !ok || !f.hasChildType(ref.Type) { + return nil, &types.NotSupported{} + } + + parent.removeChild(ref) + f.putChild(obj) + } + + return nil, nil + }) + + task.Run() + + return &methods.MoveIntoFolder_TaskBody{ + Res: &types.MoveIntoFolder_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (f *Folder) CreateDVSTask(c *types.CreateDVS_Task) soap.HasFault { + task := CreateTask(f, "createDVS", func(t *Task) (types.AnyType, types.BaseMethodFault) { + dvs := &VmwareDistributedVirtualSwitch{} + dvs.Name = c.Spec.ConfigSpec.GetDVSConfigSpec().Name + dvs.Entity().Name = dvs.Name + + if Map.FindByName(dvs.Name, f.ChildEntity) != nil { + return nil, &types.InvalidArgument{InvalidProperty: "name"} + } + + dvs.Uuid = uuid.New().String() + + f.putChild(dvs) + + return dvs.Reference(), nil + }) + + task.Run() + + return &methods.CreateDVS_TaskBody{ + Res: &types.CreateDVS_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (f *Folder) RenameTask(r *types.Rename_Task) soap.HasFault { + return RenameTask(f, r) +} diff --git a/vendor/github.com/vmware/govmomi/simulator/host_datastore_browser.go b/vendor/github.com/vmware/govmomi/simulator/host_datastore_browser.go new file mode 100644 index 00000000..d7e9ad7c --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/host_datastore_browser.go @@ -0,0 +1,258 @@ +/* +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 simulator + +import ( + "io/ioutil" + "log" + "os" + "path" + "strings" + + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type HostDatastoreBrowser struct { + mo.HostDatastoreBrowser +} + +type searchDatastore struct { + *HostDatastoreBrowser + + DatastorePath string + SearchSpec *types.HostDatastoreBrowserSearchSpec + + res []types.HostDatastoreBrowserSearchResults + + recurse bool +} + +func (s *searchDatastore) addFile(file os.FileInfo, res *types.HostDatastoreBrowserSearchResults) { + details := s.SearchSpec.Details + if details == nil { + details = new(types.FileQueryFlags) + } + + name := file.Name() + + info := types.FileInfo{ + Path: name, + } + + var finfo types.BaseFileInfo = &info + + if details.FileSize { + info.FileSize = file.Size() + } + + if details.Modification { + mtime := file.ModTime() + info.Modification = &mtime + } + + if isTrue(details.FileOwner) { + // Assume for now this process created all files in the datastore + user := os.Getenv("USER") + + info.Owner = user + } + + if file.IsDir() { + finfo = &types.FolderFileInfo{FileInfo: info} + } else if details.FileType { + switch path.Ext(name) { + case ".img": + finfo = &types.FloppyImageFileInfo{FileInfo: info} + case ".iso": + finfo = &types.IsoImageFileInfo{FileInfo: info} + case ".log": + finfo = &types.VmLogFileInfo{FileInfo: info} + case ".nvram": + finfo = &types.VmNvramFileInfo{FileInfo: info} + case ".vmdk": + // TODO: lookup device to set other fields + finfo = &types.VmDiskFileInfo{FileInfo: info} + case ".vmx": + finfo = &types.VmConfigFileInfo{FileInfo: info} + } + } + + res.File = append(res.File, finfo) +} + +func (s *searchDatastore) queryMatch(file os.FileInfo) bool { + if len(s.SearchSpec.Query) == 0 { + return true + } + + name := file.Name() + ext := path.Ext(name) + + for _, q := range s.SearchSpec.Query { + switch q.(type) { + case *types.FileQuery: + return true + case *types.FolderFileQuery: + if file.IsDir() { + return true + } + case *types.FloppyImageFileQuery: + if ext == ".img" { + return true + } + case *types.IsoImageFileQuery: + if ext == ".iso" { + return true + } + case *types.VmConfigFileQuery: + if ext == ".vmx" { + // TODO: check Filter and Details fields + return true + } + case *types.VmDiskFileQuery: + if ext == ".vmdk" { + if strings.HasSuffix(name, "-flat.vmdk") { + // only matches the descriptor, not the backing file(s) + return false + } + // TODO: check Filter and Details fields + return true + } + case *types.VmLogFileQuery: + if ext == ".log" { + return strings.HasPrefix(name, "vmware") + } + case *types.VmNvramFileQuery: + if ext == ".nvram" { + return true + } + case *types.VmSnapshotFileQuery: + if ext == ".vmsn" { + return true + } + } + } + + return false +} + +func (s *searchDatastore) search(ds *types.ManagedObjectReference, folder string, dir string) error { + files, err := ioutil.ReadDir(dir) + if err != nil { + log.Printf("search %s: %s", dir, err) + return err + } + + res := types.HostDatastoreBrowserSearchResults{ + Datastore: ds, + FolderPath: folder, + } + + for _, file := range files { + name := file.Name() + + if s.queryMatch(file) { + for _, m := range s.SearchSpec.MatchPattern { + if ok, _ := path.Match(m, name); ok { + s.addFile(file, &res) + break + } + } + } + + if s.recurse && file.IsDir() { + _ = s.search(ds, path.Join(folder, name), path.Join(dir, name)) + } + } + + s.res = append(s.res, res) + + return nil +} + +func (s *searchDatastore) Run(Task *Task) (types.AnyType, types.BaseMethodFault) { + p, fault := parseDatastorePath(s.DatastorePath) + if fault != nil { + return nil, fault + } + + ref := Map.FindByName(p.Datastore, s.Datastore) + if ref == nil { + return nil, &types.InvalidDatastore{Name: p.Datastore} + } + + ds := ref.(*Datastore) + + dir := path.Join(ds.Info.GetDatastoreInfo().Url, p.Path) + + err := s.search(&ds.Self, s.DatastorePath, dir) + if err != nil { + ff := types.FileFault{ + File: p.Path, + } + + if os.IsNotExist(err) { + return nil, &types.FileNotFound{FileFault: ff} + } + + return nil, &types.InvalidArgument{InvalidProperty: p.Path} + } + + if s.recurse { + return types.ArrayOfHostDatastoreBrowserSearchResults{ + HostDatastoreBrowserSearchResults: s.res, + }, nil + } + + return s.res[0], nil +} + +func (b *HostDatastoreBrowser) SearchDatastoreTask(s *types.SearchDatastore_Task) soap.HasFault { + task := NewTask(&searchDatastore{ + HostDatastoreBrowser: b, + DatastorePath: s.DatastorePath, + SearchSpec: s.SearchSpec, + }) + + task.Run() + + return &methods.SearchDatastore_TaskBody{ + Res: &types.SearchDatastore_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (b *HostDatastoreBrowser) SearchDatastoreSubFoldersTask(s *types.SearchDatastoreSubFolders_Task) soap.HasFault { + task := NewTask(&searchDatastore{ + HostDatastoreBrowser: b, + DatastorePath: s.DatastorePath, + SearchSpec: s.SearchSpec, + recurse: true, + }) + + task.Run() + + return &methods.SearchDatastoreSubFolders_TaskBody{ + Res: &types.SearchDatastoreSubFolders_TaskResponse{ + Returnval: task.Self, + }, + } +} diff --git a/vendor/github.com/vmware/govmomi/simulator/host_datastore_system.go b/vendor/github.com/vmware/govmomi/simulator/host_datastore_system.go new file mode 100644 index 00000000..32da7d0e --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/host_datastore_system.go @@ -0,0 +1,161 @@ +/* +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 simulator + +import ( + "os" + "path" + + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type HostDatastoreSystem struct { + mo.HostDatastoreSystem + + Host *mo.HostSystem +} + +func (dss *HostDatastoreSystem) add(ds *Datastore) *soap.Fault { + info := ds.Info.GetDatastoreInfo() + + info.Name = ds.Name + + if e := Map.FindByName(ds.Name, dss.Datastore); e != nil { + return Fault(e.Reference().Value, &types.DuplicateName{ + Name: ds.Name, + Object: e.Reference(), + }) + } + + fi, err := os.Stat(info.Url) + if err == nil && !fi.IsDir() { + err = os.ErrInvalid + } + + if err != nil { + switch { + case os.IsNotExist(err): + return Fault(err.Error(), &types.NotFound{}) + default: + return Fault(err.Error(), &types.HostConfigFault{}) + } + } + + folder := Map.getEntityFolder(dss.Host, "datastore") + ds.Self.Type = TypeName(ds) + // Datastore is the only type where create methods do not include the parent (Folder in this case), + // but we need the moref to be unique per DC/datastoreFolder, but not per-HostSystem. + ds.Self.Value += "@" + folder.Self.Value + // TODO: name should be made unique in the case of Local ds type + + ds.Summary.Datastore = &ds.Self + ds.Summary.Name = ds.Name + ds.Summary.Url = info.Url + + dss.Datastore = append(dss.Datastore, ds.Self) + dss.Host.Datastore = dss.Datastore + parent := hostParent(dss.Host) + parent.Datastore = AddReference(ds.Self, parent.Datastore) + + browser := &HostDatastoreBrowser{} + browser.Datastore = dss.Datastore + ds.Browser = Map.Put(browser).Reference() + + folder.putChild(ds) + + return nil +} + +func (dss *HostDatastoreSystem) CreateLocalDatastore(c *types.CreateLocalDatastore) soap.HasFault { + r := &methods.CreateLocalDatastoreBody{} + + ds := &Datastore{} + ds.Name = c.Name + ds.Self.Value = c.Path + + ds.Info = &types.LocalDatastoreInfo{ + DatastoreInfo: types.DatastoreInfo{ + Name: c.Name, + Url: c.Path, + }, + Path: c.Path, + } + + ds.Summary.Type = "local" + + if err := dss.add(ds); err != nil { + r.Fault_ = err + return r + } + + ds.Host = append(ds.Host, types.DatastoreHostMount{ + Key: dss.Host.Reference(), + MountInfo: types.HostMountInfo{ + AccessMode: string(types.HostMountModeReadWrite), + Mounted: types.NewBool(true), + Accessible: types.NewBool(true), + }, + }) + + _ = ds.RefreshDatastore(&types.RefreshDatastore{This: ds.Self}) + + r.Res = &types.CreateLocalDatastoreResponse{ + Returnval: ds.Self, + } + + return r +} + +func (dss *HostDatastoreSystem) CreateNasDatastore(c *types.CreateNasDatastore) soap.HasFault { + r := &methods.CreateNasDatastoreBody{} + + ds := &Datastore{} + ds.Name = path.Base(c.Spec.LocalPath) + ds.Self.Value = c.Spec.RemoteHost + ":" + c.Spec.RemotePath + + ds.Info = &types.NasDatastoreInfo{ + DatastoreInfo: types.DatastoreInfo{ + Url: c.Spec.LocalPath, + }, + Nas: &types.HostNasVolume{ + HostFileSystemVolume: types.HostFileSystemVolume{ + Name: c.Spec.LocalPath, + Type: c.Spec.Type, + }, + RemoteHost: c.Spec.RemoteHost, + RemotePath: c.Spec.RemotePath, + }, + } + + ds.Summary.Type = c.Spec.Type + + if err := dss.add(ds); err != nil { + r.Fault_ = err + return r + } + + _ = ds.RefreshDatastore(&types.RefreshDatastore{This: ds.Self}) + + r.Res = &types.CreateNasDatastoreResponse{ + Returnval: ds.Self, + } + + return r +} diff --git a/vendor/github.com/vmware/govmomi/simulator/host_firewall_system.go b/vendor/github.com/vmware/govmomi/simulator/host_firewall_system.go new file mode 100644 index 00000000..fd596386 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/host_firewall_system.go @@ -0,0 +1,87 @@ +/* +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 simulator + +import ( + "github.com/vmware/govmomi/simulator/esx" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type HostFirewallSystem struct { + mo.HostFirewallSystem +} + +func NewHostFirewallSystem(_ *mo.HostSystem) *HostFirewallSystem { + info := esx.HostFirewallInfo + + return &HostFirewallSystem{ + HostFirewallSystem: mo.HostFirewallSystem{ + FirewallInfo: &info, + }, + } +} + +func DisableRuleset(info *types.HostFirewallInfo, id string) bool { + for i := range info.Ruleset { + if info.Ruleset[i].Key == id { + info.Ruleset[i].Enabled = false + return true + } + } + + return false +} + +func (s *HostFirewallSystem) DisableRuleset(req *types.DisableRuleset) soap.HasFault { + body := &methods.DisableRulesetBody{} + + if DisableRuleset(s.HostFirewallSystem.FirewallInfo, req.Id) { + body.Res = new(types.DisableRulesetResponse) + return body + } + + body.Fault_ = Fault("", &types.NotFound{}) + + return body +} + +func EnableRuleset(info *types.HostFirewallInfo, id string) bool { + for i := range info.Ruleset { + if info.Ruleset[i].Key == id { + info.Ruleset[i].Enabled = true + return true + } + } + + return false +} + +func (s *HostFirewallSystem) EnableRuleset(req *types.EnableRuleset) soap.HasFault { + body := &methods.EnableRulesetBody{} + + if EnableRuleset(s.HostFirewallSystem.FirewallInfo, req.Id) { + body.Res = new(types.EnableRulesetResponse) + return body + } + + body.Fault_ = Fault("", &types.NotFound{}) + + return body +} diff --git a/vendor/github.com/vmware/govmomi/simulator/host_network_system.go b/vendor/github.com/vmware/govmomi/simulator/host_network_system.go new file mode 100644 index 00000000..d0502c9a --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/host_network_system.go @@ -0,0 +1,161 @@ +/* +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 simulator + +import ( + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type HostNetworkSystem struct { + mo.HostNetworkSystem + + Host *mo.HostSystem +} + +func NewHostNetworkSystem(host *mo.HostSystem) *HostNetworkSystem { + return &HostNetworkSystem{ + Host: host, + HostNetworkSystem: mo.HostNetworkSystem{ + NetworkInfo: &types.HostNetworkInfo{ + Vswitch: []types.HostVirtualSwitch{ + { + Name: "vSwitch0", + Portgroup: []string{"VM Network"}, + }, + }, + }, + }, + } +} + +func (s *HostNetworkSystem) folder() *Folder { + f := Map.getEntityDatacenter(s.Host).NetworkFolder + return Map.Get(f).(*Folder) +} + +func (s *HostNetworkSystem) AddVirtualSwitch(c *types.AddVirtualSwitch) soap.HasFault { + r := &methods.AddVirtualSwitchBody{} + + for _, vswitch := range s.NetworkInfo.Vswitch { + if vswitch.Name == c.VswitchName { + r.Fault_ = Fault("", &types.AlreadyExists{Name: c.VswitchName}) + return r + } + } + + s.NetworkInfo.Vswitch = append(s.NetworkInfo.Vswitch, types.HostVirtualSwitch{ + Name: c.VswitchName, + }) + + r.Res = &types.AddVirtualSwitchResponse{} + + return r +} + +func (s *HostNetworkSystem) RemoveVirtualSwitch(c *types.RemoveVirtualSwitch) soap.HasFault { + r := &methods.RemoveVirtualSwitchBody{} + + vs := s.NetworkInfo.Vswitch + + for i, v := range vs { + if v.Name == c.VswitchName { + s.NetworkInfo.Vswitch = append(vs[:i], vs[i+1:]...) + r.Res = &types.RemoveVirtualSwitchResponse{} + return r + } + } + + r.Fault_ = Fault("", &types.NotFound{}) + + return r +} + +func (s *HostNetworkSystem) AddPortGroup(c *types.AddPortGroup) soap.HasFault { + var vswitch *types.HostVirtualSwitch + + r := &methods.AddPortGroupBody{} + + if c.Portgrp.Name == "" { + r.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "name"}) + return r + } + + for i := range s.NetworkInfo.Vswitch { + if s.NetworkInfo.Vswitch[i].Name == c.Portgrp.VswitchName { + vswitch = &s.NetworkInfo.Vswitch[i] + break + } + } + + if vswitch == nil { + r.Fault_ = Fault("", &types.NotFound{}) + return r + } + + network := &mo.Network{} + network.Name = c.Portgrp.Name + network.Entity().Name = network.Name + + folder := s.folder() + + if obj := Map.FindByName(c.Portgrp.Name, folder.ChildEntity); obj != nil { + r.Fault_ = Fault("", &types.DuplicateName{ + Name: c.Portgrp.Name, + Object: obj.Reference(), + }) + + return r + } + + folder.putChild(network) + + vswitch.Portgroup = append(vswitch.Portgroup, c.Portgrp.Name) + r.Res = &types.AddPortGroupResponse{} + + return r +} + +func (s *HostNetworkSystem) RemovePortGroup(c *types.RemovePortGroup) soap.HasFault { + var vswitch *types.HostVirtualSwitch + + r := &methods.RemovePortGroupBody{} + + for i, v := range s.NetworkInfo.Vswitch { + for j, pg := range v.Portgroup { + if pg == c.PgName { + vswitch = &s.NetworkInfo.Vswitch[i] + vswitch.Portgroup = append(vswitch.Portgroup[:j], vswitch.Portgroup[j+1:]...) + } + } + } + + if vswitch == nil { + r.Fault_ = Fault("", &types.NotFound{}) + return r + } + + folder := s.folder() + e := Map.FindByName(c.PgName, folder.ChildEntity) + folder.removeChild(e.Reference()) + + r.Res = &types.RemovePortGroupResponse{} + + return r +} diff --git a/vendor/github.com/vmware/govmomi/simulator/host_system.go b/vendor/github.com/vmware/govmomi/simulator/host_system.go new file mode 100644 index 00000000..779c578c --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/host_system.go @@ -0,0 +1,132 @@ +/* +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 simulator + +import ( + "time" + + "github.com/google/uuid" + "github.com/vmware/govmomi/simulator/esx" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +type HostSystem struct { + mo.HostSystem +} + +func NewHostSystem(host mo.HostSystem) *HostSystem { + now := time.Now() + + host.Name = host.Summary.Config.Name + host.Summary.Runtime = &host.Runtime + host.Summary.Runtime.BootTime = &now + + hw := *host.Summary.Hardware // shallow copy + hw.Uuid = uuid.New().String() + host.Summary.Hardware = &hw + + info := *esx.HostHardwareInfo + info.SystemInfo.Uuid = hw.Uuid + host.Hardware = &info + + hs := &HostSystem{ + HostSystem: host, + } + + config := []struct { + ref **types.ManagedObjectReference + obj mo.Reference + }{ + {&hs.ConfigManager.DatastoreSystem, &HostDatastoreSystem{Host: &hs.HostSystem}}, + {&hs.ConfigManager.NetworkSystem, NewHostNetworkSystem(&hs.HostSystem)}, + {&hs.ConfigManager.AdvancedOption, NewOptionManager(nil, esx.Setting)}, + {&hs.ConfigManager.FirewallSystem, NewHostFirewallSystem(&hs.HostSystem)}, + } + + for _, c := range config { + ref := Map.Put(c.obj).Reference() + + *c.ref = &ref + } + + return hs +} + +func hostParent(host *mo.HostSystem) *mo.ComputeResource { + switch parent := Map.Get(*host.Parent).(type) { + case *mo.ComputeResource: + return parent + case *ClusterComputeResource: + return &parent.ComputeResource + default: + return nil + } +} + +// CreateDefaultESX creates a standalone ESX +// Adds objects of type: Datacenter, Network, ComputeResource, ResourcePool and HostSystem +func CreateDefaultESX(f *Folder) { + dc := &esx.Datacenter + f.putChild(dc) + createDatacenterFolders(dc, false) + + host := NewHostSystem(esx.HostSystem) + + cr := &mo.ComputeResource{} + cr.Self = *host.Parent + cr.Name = host.Name + cr.Host = append(cr.Host, host.Reference()) + Map.PutEntity(cr, host) + + pool := NewResourcePool() + cr.ResourcePool = &pool.Self + Map.PutEntity(cr, pool) + pool.Owner = cr.Self + + Map.Get(dc.HostFolder).(*Folder).putChild(cr) +} + +// CreateStandaloneHost uses esx.HostSystem as a template, applying the given spec +// and creating the ComputeResource parent and ResourcePool sibling. +func CreateStandaloneHost(f *Folder, spec types.HostConnectSpec) (*HostSystem, types.BaseMethodFault) { + if spec.HostName == "" { + return nil, &types.NoHost{} + } + + pool := NewResourcePool() + host := NewHostSystem(esx.HostSystem) + + host.Summary.Config.Name = spec.HostName + host.Name = host.Summary.Config.Name + host.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected + + cr := &mo.ComputeResource{} + + Map.PutEntity(cr, Map.NewEntity(host)) + + Map.PutEntity(cr, Map.NewEntity(pool)) + + cr.Name = host.Name + cr.Host = append(cr.Host, host.Reference()) + cr.ResourcePool = &pool.Self + + f.putChild(cr) + pool.Owner = cr.Self + + return host, nil +} diff --git a/vendor/github.com/vmware/govmomi/simulator/license_manager.go b/vendor/github.com/vmware/govmomi/simulator/license_manager.go new file mode 100644 index 00000000..adabfc54 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/license_manager.go @@ -0,0 +1,111 @@ +/* +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. +*/ +// Copyright 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 simulator + +import ( + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +// EvalLicense is the default license +var EvalLicense = types.LicenseManagerLicenseInfo{ + LicenseKey: "00000-00000-00000-00000-00000", + EditionKey: "eval", + Name: "Evaluation Mode", + Properties: []types.KeyAnyValue{ + { + Key: "feature", + Value: types.KeyValue{ + Key: "serialuri:2", + Value: "Remote virtual Serial Port Concentrator", + }, + }, + { + Key: "feature", + Value: types.KeyValue{ + Key: "dvs", + Value: "vSphere Distributed Switch", + }, + }, + }, +} + +type LicenseManager struct { + mo.LicenseManager +} + +func NewLicenseManager(ref types.ManagedObjectReference) object.Reference { + m := &LicenseManager{} + m.Self = ref + m.Licenses = []types.LicenseManagerLicenseInfo{EvalLicense} + + if Map.IsVPX() { + am := Map.Put(&LicenseAssignmentManager{}).Reference() + m.LicenseAssignmentManager = &am + } + + return m +} + +type LicenseAssignmentManager struct { + mo.LicenseAssignmentManager +} + +func (m *LicenseAssignmentManager) QueryAssignedLicenses(req *types.QueryAssignedLicenses) soap.HasFault { + body := &methods.QueryAssignedLicensesBody{ + Res: &types.QueryAssignedLicensesResponse{}, + } + + // EntityId can be a HostSystem or the vCenter InstanceUuid + if req.EntityId != "" { + if req.EntityId != Map.content().About.InstanceUuid { + id := types.ManagedObjectReference{ + Type: "HostSystem", + Value: req.EntityId, + } + + if Map.Get(id) == nil { + return body + } + } + } + + body.Res.Returnval = []types.LicenseAssignmentManagerLicenseAssignment{ + { + EntityId: req.EntityId, + AssignedLicense: EvalLicense, + }, + } + + return body +} diff --git a/vendor/github.com/vmware/govmomi/simulator/model.go b/vendor/github.com/vmware/govmomi/simulator/model.go new file mode 100644 index 00000000..18daccbd --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/model.go @@ -0,0 +1,473 @@ +/* +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 simulator + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "path" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/simulator/esx" + "github.com/vmware/govmomi/simulator/vpx" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +// Model is used to populate a Model with an initial set of managed entities. +// This is a simple helper for tests running against a simulator, to populate an inventory +// with commonly used models. +type Model struct { + Service *Service + + ServiceContent types.ServiceContent + RootFolder mo.Folder + + // Datacenter specifies the number of Datacenter entities to create + Datacenter int + + // Portgroup specifies the number of DistributedVirtualPortgroup entities to create per Datacenter + Portgroup int + + // Host specifies the number of standalone HostSystems entities to create per Datacenter + Host int + + // Cluster specifies the number of ClusterComputeResource entities to create per Datacenter + Cluster int + + // ClusterHost specifies the number of HostSystems entities to create within a Cluster + ClusterHost int + + // Pool specifies the number of ResourcePool entities to create per Cluster + Pool int + + // Datastore specifies the number of Datastore entities to create + // Each Datastore will have temporary local file storage and will be mounted + // on every HostSystem created by the ModelConfig + Datastore int + + // Machine specifies the number of VirtualMachine entities to create per ResourcePool + Machine int + + // Folder specifies the number of Datacenter to place within a Folder. + // This includes a folder for the Datacenter itself and its host, vm, network and datastore folders. + // All resources for the Datacenter are placed within these folders, rather than the top-level folders. + Folder int + + // App specifies the number of VirtualApp to create per Cluster + App int + + // Pod specifies the number of StoragePod to create per Cluster + Pod int + + // total number of inventory objects, set by Count() + total int + + dirs []string +} + +// ESX is the default Model for a standalone ESX instance +func ESX() *Model { + return &Model{ + ServiceContent: esx.ServiceContent, + RootFolder: esx.RootFolder, + Datastore: 1, + Machine: 2, + } +} + +// VPX is the default Model for a vCenter instance +func VPX() *Model { + return &Model{ + ServiceContent: vpx.ServiceContent, + RootFolder: vpx.RootFolder, + Datacenter: 1, + Portgroup: 1, + Host: 1, + Cluster: 1, + ClusterHost: 3, + Datastore: 1, + Machine: 2, + } +} + +// Count returns a Model with total number of each existing type +func (m *Model) Count() Model { + count := Model{} + + for ref, obj := range Map.objects { + if _, ok := obj.(mo.Entity); !ok { + continue + } + + count.total++ + + switch ref.Type { + case "Datacenter": + count.Datacenter++ + case "DistributedVirtualPortgroup": + count.Portgroup++ + case "ClusterComputeResource": + count.Cluster++ + case "Datastore": + count.Datastore++ + case "HostSystem": + count.Host++ + case "VirtualMachine": + count.Machine++ + case "ResourcePool": + count.Pool++ + case "VirtualApp": + count.App++ + case "Folder": + count.Folder++ + case "StoragePod": + count.Pod++ + } + } + + return count +} + +func (*Model) fmtName(prefix string, num int) string { + return fmt.Sprintf("%s%d", prefix, num) +} + +// Create populates the Model with the given ModelConfig +func (m *Model) Create() error { + m.Service = New(NewServiceInstance(m.ServiceContent, m.RootFolder)) + + ctx := context.Background() + client := m.Service.client + root := object.NewRootFolder(client) + + // After all hosts are created, this var is used to mount the host datastores. + var hosts []*object.HostSystem + // We need to defer VM creation until after the datastores are created. + var vms []func() error + // 1 DVS per DC, added to all hosts + var dvs *object.DistributedVirtualSwitch + // 1 NIC per VM, backed by a DVPG if Model.Portgroup > 0 + vmnet := esx.EthernetCard.Backing + + // addHost adds a cluster host or a stanalone host. + addHost := func(name string, f func(types.HostConnectSpec) (*object.Task, error)) (*object.HostSystem, error) { + spec := types.HostConnectSpec{ + HostName: name, + } + + task, err := f(spec) + if err != nil { + return nil, err + } + + info, err := task.WaitForResult(context.Background(), nil) + if err != nil { + return nil, err + } + + host := object.NewHostSystem(client, info.Result.(types.ManagedObjectReference)) + hosts = append(hosts, host) + + if dvs != nil { + config := &types.DVSConfigSpec{ + Host: []types.DistributedVirtualSwitchHostMemberConfigSpec{{ + Operation: string(types.ConfigSpecOperationAdd), + Host: host.Reference(), + }}, + } + + _, _ = dvs.Reconfigure(ctx, config) + } + + return host, nil + } + + // addMachine returns a func to create a VM. + addMachine := func(prefix string, host *object.HostSystem, pool *object.ResourcePool, folders *object.DatacenterFolders) { + nic := esx.EthernetCard + nic.Backing = vmnet + ds := types.ManagedObjectReference{} + + f := func() error { + for i := 0; i < m.Machine; i++ { + name := m.fmtName(prefix+"_VM", i) + + config := types.VirtualMachineConfigSpec{ + Name: name, + GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest), + Files: &types.VirtualMachineFileInfo{ + VmPathName: "[LocalDS_0]", + }, + } + + if pool == nil { + pool, _ = host.ResourcePool(ctx) + } + + var devices object.VirtualDeviceList + + scsi, _ := devices.CreateSCSIController("pvscsi") + ide, _ := devices.CreateIDEController() + cdrom, _ := devices.CreateCdrom(ide.(*types.VirtualIDEController)) + disk := devices.CreateDisk(scsi.(types.BaseVirtualController), ds, + config.Files.VmPathName+" "+path.Join(name, "disk1.vmdk")) + + devices = append(devices, scsi, cdrom, disk, &nic) + + config.DeviceChange, _ = devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd) + + task, err := folders.VmFolder.CreateVM(ctx, config, pool, host) + if err != nil { + return err + } + + err = task.Wait(ctx) + if err != nil { + return err + } + } + + return nil + } + + vms = append(vms, f) + } + + nfolder := 0 + + for ndc := 0; ndc < m.Datacenter; ndc++ { + dcName := m.fmtName("DC", ndc) + folder := root + fName := m.fmtName("F", nfolder) + + // If Datacenter > Folder, don't create folders for the first N DCs. + if nfolder < m.Folder && ndc >= (m.Datacenter-m.Folder) { + f, err := folder.CreateFolder(ctx, fName) + if err != nil { + return err + } + folder = f + } + + dc, err := folder.CreateDatacenter(ctx, dcName) + if err != nil { + return err + } + + folders, err := dc.Folders(ctx) + if err != nil { + return err + } + + if m.Pod > 0 { + for pod := 0; pod < m.Pod; pod++ { + _, _ = folders.DatastoreFolder.CreateStoragePod(ctx, m.fmtName(dcName+"_POD", pod)) + } + } + + if folder != root { + // Create sub-folders and use them to create any resources that follow + subs := []**object.Folder{&folders.DatastoreFolder, &folders.HostFolder, &folders.NetworkFolder, &folders.VmFolder} + + for _, sub := range subs { + f, err := (*sub).CreateFolder(ctx, fName) + if err != nil { + return err + } + + *sub = f + } + + nfolder++ + } + + if m.Portgroup > 0 { + var spec types.DVSCreateSpec + spec.ConfigSpec = &types.VMwareDVSConfigSpec{} + spec.ConfigSpec.GetDVSConfigSpec().Name = m.fmtName("DVS", 0) + + task, err := folders.NetworkFolder.CreateDVS(ctx, spec) + if err != nil { + return err + } + + info, err := task.WaitForResult(ctx, nil) + if err != nil { + return err + } + + dvs = object.NewDistributedVirtualSwitch(client, info.Result.(types.ManagedObjectReference)) + + for npg := 0; npg < m.Portgroup; npg++ { + name := m.fmtName(dcName+"_DVPG", npg) + + task, err = dvs.AddPortgroup(ctx, []types.DVPortgroupConfigSpec{{Name: name}}) + if err != nil { + return err + } + + err = task.Wait(ctx) + if err != nil { + return err + } + + // Use the 1st DVPG for the VMs eth0 backing + if npg == 0 { + // AddPortgroup_Task does not return the moid, so we look it up by name + net := Map.Get(folders.NetworkFolder.Reference()).(*Folder) + pg := Map.FindByName(name, net.ChildEntity) + + vmnet, _ = object.NewDistributedVirtualPortgroup(client, pg.Reference()).EthernetCardBackingInfo(ctx) + } + } + } + + for nhost := 0; nhost < m.Host; nhost++ { + name := m.fmtName(dcName+"_H", nhost) + + host, err := addHost(name, func(spec types.HostConnectSpec) (*object.Task, error) { + return folders.HostFolder.AddStandaloneHost(ctx, spec, true, nil, nil) + }) + if err != nil { + return err + } + + addMachine(name, host, nil, folders) + } + + for ncluster := 0; ncluster < m.Cluster; ncluster++ { + clusterName := m.fmtName(dcName+"_C", ncluster) + + cluster, err := folders.HostFolder.CreateCluster(ctx, clusterName, types.ClusterConfigSpecEx{}) + if err != nil { + return err + } + + for nhost := 0; nhost < m.ClusterHost; nhost++ { + name := m.fmtName(clusterName+"_H", nhost) + + _, err = addHost(name, func(spec types.HostConnectSpec) (*object.Task, error) { + return cluster.AddHost(ctx, spec, true, nil, nil) + }) + if err != nil { + return err + } + } + + pool, err := cluster.ResourcePool(ctx) + if err != nil { + return err + } + + prefix := clusterName + "_RP" + + addMachine(prefix+"0", nil, pool, folders) + + for npool := 1; npool <= m.Pool; npool++ { + spec := NewResourceConfigSpec() + + _, err = pool.Create(ctx, m.fmtName(prefix, npool), spec) + if err != nil { + return err + } + } + + prefix = clusterName + "_APP" + + for napp := 0; napp < m.App; napp++ { + rspec := NewResourceConfigSpec() + vspec := NewVAppConfigSpec() + name := m.fmtName(prefix, napp) + + vapp, err := pool.CreateVApp(ctx, name, rspec, vspec, nil) + if err != nil { + return err + } + + addMachine(name, nil, vapp.ResourcePool, folders) + } + } + } + + if m.ServiceContent.RootFolder == esx.RootFolder.Reference() { + // ESX model + host := object.NewHostSystem(client, esx.HostSystem.Reference()) + hosts = append(hosts, host) + + dc := object.NewDatacenter(client, esx.Datacenter.Reference()) + folders, err := dc.Folders(ctx) + if err != nil { + return err + } + + addMachine(host.Reference().Value, host, nil, folders) + } + + for i := 0; i < m.Datastore; i++ { + err := m.createLocalDatastore(m.fmtName("LocalDS_", i), hosts) + if err != nil { + return err + } + } + + for _, createVM := range vms { + err := createVM() + if err != nil { + return err + } + } + + return nil +} + +var tempDir = func() (string, error) { + return ioutil.TempDir("", "govcsim-") +} + +func (m *Model) createLocalDatastore(name string, hosts []*object.HostSystem) error { + ctx := context.Background() + dir, err := tempDir() + if err != nil { + return err + } + + m.dirs = append(m.dirs, dir) + + for _, host := range hosts { + dss, err := host.ConfigManager().DatastoreSystem(ctx) + if err != nil { + return err + } + + _, err = dss.CreateLocalDatastore(ctx, name, dir) + if err != nil { + return err + } + } + + return nil +} + +// Remove cleans up items created by the Model, such as local datastore directories +func (m *Model) Remove() { + for _, dir := range m.dirs { + _ = os.RemoveAll(dir) + } +} diff --git a/vendor/github.com/vmware/govmomi/simulator/option_manager.go b/vendor/github.com/vmware/govmomi/simulator/option_manager.go new file mode 100644 index 00000000..4615882c --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/option_manager.go @@ -0,0 +1,59 @@ +/* +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 simulator + +import ( + "strings" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type OptionManager struct { + mo.OptionManager +} + +func NewOptionManager(ref *types.ManagedObjectReference, setting []types.BaseOptionValue) object.Reference { + s := &OptionManager{} + if ref != nil { + s.Self = *ref + } + s.Setting = setting + return s +} + +func (m *OptionManager) QueryOptions(req *types.QueryOptions) soap.HasFault { + body := &methods.QueryOptionsBody{} + res := &types.QueryOptionsResponse{} + + for _, opt := range m.Setting { + if strings.HasPrefix(opt.GetOptionValue().Key, req.Name) { + res.Returnval = append(res.Returnval, opt) + } + } + + if len(res.Returnval) == 0 { + body.Fault_ = Fault("", &types.InvalidName{Name: req.Name}) + } else { + body.Res = res + } + + return body +} diff --git a/vendor/github.com/vmware/govmomi/simulator/property_collector.go b/vendor/github.com/vmware/govmomi/simulator/property_collector.go new file mode 100644 index 00000000..f2b8d0a7 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/property_collector.go @@ -0,0 +1,539 @@ +/* +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 simulator + +import ( + "errors" + "log" + "path" + "reflect" + "strings" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type PropertyCollector struct { + mo.PropertyCollector +} + +func NewPropertyCollector(ref types.ManagedObjectReference) object.Reference { + s := &PropertyCollector{} + s.Self = ref + return s +} + +var errMissingField = errors.New("missing field") +var errEmptyField = errors.New("empty field") + +func getObject(ref types.ManagedObjectReference) (reflect.Value, bool) { + obj := Map.Get(ref) + if obj == nil { + return reflect.Value{}, false + } + + rval := reflect.ValueOf(obj).Elem() + rtype := rval.Type() + + // PropertyCollector is for Managed Object types only (package mo). + // If the registry object is not in the mo package, assume it is a wrapper + // type where the first field is an embedded mo type. + // We need to dig out the mo type for PropSet.All to work properly and + // for the case where the type has a field of the same name, for example: + // mo.ResourcePool.ResourcePool + for { + if path.Base(rtype.PkgPath()) != "mo" { + if rtype.Kind() != reflect.Struct || rtype.NumField() == 0 { + log.Printf("%#v does not have an embedded mo type", ref) + return reflect.Value{}, false + } + rval = rval.Field(0) + rtype = rval.Type() + } else { + break + } + } + + return rval, true +} + +func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{} { + if rval.Kind() == reflect.Ptr { + rval = rval.Elem() + } + + pval := rval.Interface() + + if rval.Kind() == reflect.Slice { + // Convert slice to types.ArrayOf* + switch v := pval.(type) { + case []string: + pval = &types.ArrayOfString{ + String: v, + } + case []int32: + pval = &types.ArrayOfInt{ + Int: v, + } + default: + kind := f.Type.Elem().Name() + // Remove govmomi interface prefix name + if strings.HasPrefix(kind, "Base") { + kind = kind[4:] + } + akind, _ := typeFunc("ArrayOf" + kind) + a := reflect.New(akind) + a.Elem().FieldByName(kind).Set(rval) + pval = a.Interface() + } + } + + return pval +} + +func fieldValue(rval reflect.Value, p string) (interface{}, error) { + var value interface{} + + fields := strings.Split(p, ".") + + for i, name := range fields { + kind := rval.Type().Kind() + if kind == reflect.Ptr || kind == reflect.Interface { + if rval.IsNil() { + continue + } + rval = rval.Elem() + } + + x := ucFirst(name) + val := rval.FieldByName(x) + if !val.IsValid() { + return nil, errMissingField + } + + if isEmpty(val) { + return nil, errEmptyField + } + + if i == len(fields)-1 { + ftype, _ := rval.Type().FieldByName(x) + value = fieldValueInterface(ftype, val) + break + } + + rval = val + } + + return value, nil +} + +func fieldRefs(f interface{}) []types.ManagedObjectReference { + switch fv := f.(type) { + case types.ManagedObjectReference: + return []types.ManagedObjectReference{fv} + case *types.ArrayOfManagedObjectReference: + return fv.ManagedObjectReference + case nil: + // empty field + } + + return nil +} + +func isEmpty(rval reflect.Value) bool { + switch rval.Kind() { + case reflect.Ptr: + return rval.IsNil() + case reflect.String, reflect.Slice: + return rval.Len() == 0 + } + + return false +} + +func isTrue(v *bool) bool { + return v != nil && *v +} + +func isFalse(v *bool) bool { + return v == nil || *v == false +} + +func lcFirst(s string) string { + return strings.ToLower(s[:1]) + s[1:] +} + +func ucFirst(s string) string { + return strings.ToUpper(s[:1]) + s[1:] +} + +type retrieveResult struct { + *types.RetrieveResult + req *types.RetrievePropertiesEx + collected map[types.ManagedObjectReference]bool + specs map[string]*types.TraversalSpec +} + +func (rr *retrieveResult) collectAll(rval reflect.Value, rtype reflect.Type, content *types.ObjectContent) { + for i := 0; i < rval.NumField(); i++ { + val := rval.Field(i) + + f := rtype.Field(i) + + if isEmpty(val) || f.Name == "Self" { + continue + } + + if f.Anonymous { + // recurse into embedded field + rr.collectAll(val, f.Type, content) + continue + } + + content.PropSet = append(content.PropSet, types.DynamicProperty{ + Name: lcFirst(f.Name), + Val: fieldValueInterface(f, val), + }) + } +} + +func (rr *retrieveResult) collectFields(rval reflect.Value, fields []string, content *types.ObjectContent) { + seen := make(map[string]bool) + + for i := range content.PropSet { + seen[content.PropSet[i].Name] = true // mark any already collected via embedded field + } + + for _, name := range fields { + if seen[name] { + // rvc 'ls' includes the "name" property twice, then fails with no error message or stack trace + // in RbVmomi::VIM::ObjectContent.to_hash_uncached when it sees the 2nd "name" property. + continue + } + seen[name] = true + + val, err := fieldValue(rval, name) + if err == nil { + prop := types.DynamicProperty{ + Name: name, + Val: val, + } + + content.PropSet = append(content.PropSet, prop) + continue + } + + switch err { + case errEmptyField: + // ok + case errMissingField: + content.MissingSet = append(content.MissingSet, types.MissingProperty{ + Path: name, + Fault: types.LocalizedMethodFault{Fault: &types.InvalidProperty{ + Name: name, + }}, + }) + } + } +} + +func (rr *retrieveResult) collect(ref types.ManagedObjectReference) { + if rr.collected[ref] { + return + } + + content := types.ObjectContent{ + Obj: ref, + } + + rval, ok := getObject(ref) + if !ok { + // Possible if a test uses Map.Remove instead of Destroy_Task + log.Printf("object %s no longer exists", ref) + return + } + + rtype := rval.Type() + + for _, spec := range rr.req.SpecSet { + for _, p := range spec.PropSet { + if p.Type != ref.Type { + // e.g. ManagedEntity, ComputeResource + field, ok := rtype.FieldByName(p.Type) + + if !(ok && field.Anonymous) { + continue + } + } + + if isTrue(p.All) { + rr.collectAll(rval, rtype, &content) + continue + } + + rr.collectFields(rval, p.PathSet, &content) + } + } + + if len(content.PropSet) != 0 || len(content.MissingSet) != 0 { + rr.Objects = append(rr.Objects, content) + } + + rr.collected[ref] = true +} + +func (rr *retrieveResult) selectSet(obj reflect.Value, s []types.BaseSelectionSpec, refs *[]types.ManagedObjectReference) types.BaseMethodFault { + for _, ss := range s { + ts, ok := ss.(*types.TraversalSpec) + + if ok { + if ts.Name != "" { + rr.specs[ts.Name] = ts + } + } + } + + for _, ss := range s { + ts, ok := ss.(*types.TraversalSpec) + if !ok { + ts = rr.specs[ss.GetSelectionSpec().Name] + if ts == nil { + return &types.InvalidArgument{InvalidProperty: "undefined TraversalSpec name"} + } + } + + f, _ := fieldValue(obj, ts.Path) + + for _, ref := range fieldRefs(f) { + if isFalse(ts.Skip) { + *refs = append(*refs, ref) + } + + rval, ok := getObject(ref) + if ok { + if err := rr.selectSet(rval, ts.SelectSet, refs); err != nil { + return err + } + } + } + } + + return nil +} + +func (pc *PropertyCollector) collect(r *types.RetrievePropertiesEx) (*types.RetrieveResult, types.BaseMethodFault) { + var refs []types.ManagedObjectReference + + rr := &retrieveResult{ + RetrieveResult: &types.RetrieveResult{}, + req: r, + collected: make(map[types.ManagedObjectReference]bool), + specs: make(map[string]*types.TraversalSpec), + } + + // Select object references + for _, spec := range r.SpecSet { + for _, o := range spec.ObjectSet { + rval, ok := getObject(o.Obj) + + if !ok { + if isFalse(spec.ReportMissingObjectsInResults) { + return nil, &types.ManagedObjectNotFound{Obj: o.Obj} + } + continue + } + + if o.SelectSet == nil || isFalse(o.Skip) { + refs = append(refs, o.Obj) + } + + if err := rr.selectSet(rval, o.SelectSet, &refs); err != nil { + return nil, err + } + } + } + + for _, ref := range refs { + rr.collect(ref) + } + + return rr.RetrieveResult, nil +} + +func (pc *PropertyCollector) CreateFilter(c *types.CreateFilter) soap.HasFault { + body := &methods.CreateFilterBody{} + + filter := &PropertyFilter{pc: pc} + filter.PartialUpdates = c.PartialUpdates + filter.Spec = c.Spec + + pc.Filter = append(pc.Filter, Map.Put(filter).Reference()) + + body.Res = &types.CreateFilterResponse{ + Returnval: filter.Self, + } + + return body +} + +func (pc *PropertyCollector) CreatePropertyCollector(c *types.CreatePropertyCollector) soap.HasFault { + body := &methods.CreatePropertyCollectorBody{} + + cpc := &PropertyCollector{} + + body.Res = &types.CreatePropertyCollectorResponse{ + Returnval: Map.Put(cpc).Reference(), + } + + return body +} + +func (pc *PropertyCollector) DestroyPropertyCollector(c *types.DestroyPropertyCollector) soap.HasFault { + body := &methods.DestroyPropertyCollectorBody{} + + for _, ref := range pc.Filter { + filter := Map.Get(ref).(*PropertyFilter) + filter.DestroyPropertyFilter(&types.DestroyPropertyFilter{This: ref}) + } + + Map.Remove(c.This) + + body.Res = &types.DestroyPropertyCollectorResponse{} + + return body +} + +func (pc *PropertyCollector) RetrievePropertiesEx(r *types.RetrievePropertiesEx) soap.HasFault { + body := &methods.RetrievePropertiesExBody{} + + res, fault := pc.collect(r) + + if fault != nil { + body.Fault_ = Fault("", fault) + } else { + body.Res = &types.RetrievePropertiesExResponse{ + Returnval: res, + } + } + + return body +} + +// RetrieveProperties is deprecated, but govmomi is still using it at the moment. +func (pc *PropertyCollector) RetrieveProperties(r *types.RetrieveProperties) soap.HasFault { + body := &methods.RetrievePropertiesBody{} + + res := pc.RetrievePropertiesEx(&types.RetrievePropertiesEx{ + This: r.This, + SpecSet: r.SpecSet, + }) + + if res.Fault() != nil { + body.Fault_ = res.Fault() + } else { + body.Res = &types.RetrievePropertiesResponse{ + Returnval: res.(*methods.RetrievePropertiesExBody).Res.Returnval.Objects, + } + } + + return body +} + +func (pc *PropertyCollector) CancelWaitForUpdates(r *types.CancelWaitForUpdates) soap.HasFault { + return &methods.CancelWaitForUpdatesBody{Res: new(types.CancelWaitForUpdatesResponse)} +} + +func (pc *PropertyCollector) WaitForUpdatesEx(r *types.WaitForUpdatesEx) soap.HasFault { + body := &methods.WaitForUpdatesExBody{} + + // At the moment we need to support Task completion. Handlers can simply set the Task + // state before returning and the non-incremental update is enough for the client. + // We can wait for incremental updates to simulate timeouts, etc. + if r.Version != "" { + body.Fault_ = Fault("incremental updates not supported yet", &types.NotSupported{}) + return body + } + + update := &types.UpdateSet{ + Version: "-", + } + + for _, ref := range pc.Filter { + filter := Map.Get(ref).(*PropertyFilter) + + r := &types.RetrievePropertiesEx{} + r.SpecSet = append(r.SpecSet, filter.Spec) + + res, fault := pc.collect(r) + if fault != nil { + body.Fault_ = Fault("", fault) + return body + } + + fu := types.PropertyFilterUpdate{ + Filter: ref, + } + + for _, o := range res.Objects { + ou := types.ObjectUpdate{ + Obj: o.Obj, + Kind: types.ObjectUpdateKindEnter, + } + + for _, p := range o.PropSet { + ou.ChangeSet = append(ou.ChangeSet, types.PropertyChange{ + Op: types.PropertyChangeOpAssign, + Name: p.Name, + Val: p.Val, + }) + } + + fu.ObjectSet = append(fu.ObjectSet, ou) + } + + update.FilterSet = append(update.FilterSet, fu) + } + + body.Res = &types.WaitForUpdatesExResponse{ + Returnval: update, + } + + return body +} + +// WaitForUpdates is deprecated, but pyvmomi is still using it at the moment. +func (pc *PropertyCollector) WaitForUpdates(r *types.WaitForUpdates) soap.HasFault { + body := &methods.WaitForUpdatesBody{} + + res := pc.WaitForUpdatesEx(&types.WaitForUpdatesEx{ + This: r.This, + Version: r.Version, + }) + + if res.Fault() != nil { + body.Fault_ = res.Fault() + } else { + body.Res = &types.WaitForUpdatesResponse{ + Returnval: *res.(*methods.WaitForUpdatesExBody).Res.Returnval, + } + } + + return body +} diff --git a/vendor/github.com/vmware/govmomi/simulator/property_filter.go b/vendor/github.com/vmware/govmomi/simulator/property_filter.go new file mode 100644 index 00000000..0d7d9a38 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/property_filter.go @@ -0,0 +1,42 @@ +/* +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 simulator + +import ( + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type PropertyFilter struct { + mo.PropertyFilter + + pc *PropertyCollector +} + +func (f *PropertyFilter) DestroyPropertyFilter(c *types.DestroyPropertyFilter) soap.HasFault { + body := &methods.DestroyPropertyFilterBody{} + + f.pc.Filter = RemoveReference(c.This, f.pc.Filter) + + Map.Remove(c.This) + + body.Res = &types.DestroyPropertyFilterResponse{} + + return body +} diff --git a/vendor/github.com/vmware/govmomi/simulator/registry.go b/vendor/github.com/vmware/govmomi/simulator/registry.go new file mode 100644 index 00000000..8384b7a6 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/registry.go @@ -0,0 +1,309 @@ +/* +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 simulator + +import ( + "fmt" + "reflect" + "strings" + "sync" + + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +// Map is the default Registry instance. +var Map = NewRegistry() + +// RegisterObject interface supports callbacks when objects are added and removed from the Registry +type RegisterObject interface { + mo.Reference + PutObject(mo.Reference) + RemoveObject(types.ManagedObjectReference) +} + +// Registry manages a map of mo.Reference objects +type Registry struct { + m sync.Mutex + objects map[types.ManagedObjectReference]mo.Reference + handlers map[types.ManagedObjectReference]RegisterObject + counter int +} + +// NewRegistry creates a new instances of Registry +func NewRegistry() *Registry { + r := &Registry{ + objects: make(map[types.ManagedObjectReference]mo.Reference), + handlers: make(map[types.ManagedObjectReference]RegisterObject), + } + + return r +} + +// TypeName returns the type of the given object. +func TypeName(item mo.Reference) string { + return reflect.TypeOf(item).Elem().Name() +} + +// newReference returns a new MOR, where Type defaults to type of the given item +// and Value defaults to a unique id for the given type. +func (r *Registry) newReference(item mo.Reference) types.ManagedObjectReference { + ref := item.Reference() + + if ref.Type == "" { + ref.Type = TypeName(item) + } + + if ref.Value == "" { + r.counter++ + ref.Value = fmt.Sprintf("%s-%d", strings.ToLower(ref.Type), r.counter) + } + + return ref +} + +// AddHandler adds a RegisterObject handler to the Registry. +func (r *Registry) AddHandler(h RegisterObject) { + r.handlers[h.Reference()] = h +} + +// NewEntity sets Entity().Self with a new, unique Value. +// Useful for creating object instances from templates. +func (r *Registry) NewEntity(item mo.Entity) mo.Entity { + e := item.Entity() + e.Self.Value = "" + e.Self = r.newReference(item) + return item +} + +// PutEntity sets item.Parent to that of parent.Self before adding item to the Registry. +func (r *Registry) PutEntity(parent mo.Entity, item mo.Entity) mo.Entity { + e := item.Entity() + + if parent != nil { + e.Parent = &parent.Entity().Self + } + + r.Put(item) + + return item +} + +// Get returns the object for the given reference. +func (r *Registry) Get(ref types.ManagedObjectReference) mo.Reference { + r.m.Lock() + defer r.m.Unlock() + + return r.objects[ref] +} + +// Any returns the first instance of entity type specified by kind. +func (r *Registry) Any(kind string) mo.Entity { + r.m.Lock() + defer r.m.Unlock() + + for ref, val := range r.objects { + if ref.Type == kind { + return val.(mo.Entity) + } + } + + return nil +} + +// Put adds a new object to Registry, generating a ManagedObjectReference if not already set. +func (r *Registry) Put(item mo.Reference) mo.Reference { + r.m.Lock() + defer r.m.Unlock() + + ref := item.Reference() + if ref.Type == "" || ref.Value == "" { + ref = r.newReference(item) + // mo.Reference() returns a value, not a pointer so use reflect to set the Self field + reflect.ValueOf(item).Elem().FieldByName("Self").Set(reflect.ValueOf(ref)) + } + + r.objects[ref] = item + + for _, h := range r.handlers { + h.PutObject(item) + } + + return item +} + +// Remove removes an object from the Registry. +func (r *Registry) Remove(item types.ManagedObjectReference) { + r.m.Lock() + defer r.m.Unlock() + + for _, h := range r.handlers { + h.RemoveObject(item) + } + + delete(r.objects, item) + delete(r.handlers, item) +} + +// getEntityParent traverses up the inventory and returns the first object of type kind. +// If no object of type kind is found, the method will panic when it reaches the +// inventory root Folder where the Parent field is nil. +func (r *Registry) getEntityParent(item mo.Entity, kind string) mo.Entity { + for { + parent := item.Entity().Parent + + item = r.Get(*parent).(mo.Entity) + + if item.Reference().Type == kind { + return item + } + } +} + +// getEntityDatacenter returns the Datacenter containing the given item +func (r *Registry) getEntityDatacenter(item mo.Entity) *mo.Datacenter { + return r.getEntityParent(item, "Datacenter").(*mo.Datacenter) +} + +func (r *Registry) getEntityFolder(item mo.Entity, kind string) *Folder { + dc := Map.getEntityDatacenter(item) + + var ref types.ManagedObjectReference + + switch kind { + case "datastore": + ref = dc.DatastoreFolder + } + + folder := r.Get(ref).(*Folder) + + // If Model was created with Folder option, use that Folder; else use top-level folder + for _, child := range folder.ChildEntity { + if child.Type == "Folder" { + folder = Map.Get(child).(*Folder) + break + } + } + + return folder +} + +// getEntityComputeResource returns the ComputeResource parent for the given item. +// A ResourcePool for example may have N Parents of type ResourcePool, but the top +// most Parent pool is always a ComputeResource child. +func (r *Registry) getEntityComputeResource(item mo.Entity) mo.Entity { + for { + parent := item.Entity().Parent + + item = r.Get(*parent).(mo.Entity) + + switch item.Reference().Type { + case "ComputeResource": + return item + case "ClusterComputeResource": + return item + } + } +} + +// FindByName returns the first mo.Entity of the given refs whose Name field is equal to the given name. +// If there is no match, nil is returned. +// This method is useful for cases where objects are required to have a unique name, such as Datastore with +// a HostStorageSystem or HostSystem within a ClusterComputeResource. +func (r *Registry) FindByName(name string, refs []types.ManagedObjectReference) mo.Entity { + for _, ref := range refs { + if e, ok := r.Get(ref).(mo.Entity); ok { + if name == e.Entity().Name { + return e + } + } + } + + return nil +} + +// FindReference returns the 1st match found in refs, or nil if not found. +func FindReference(refs []types.ManagedObjectReference, match ...types.ManagedObjectReference) *types.ManagedObjectReference { + for _, ref := range refs { + for _, m := range match { + if ref == m { + return &ref + } + } + } + + return nil +} + +// RemoveReference returns a slice with ref removed from refs +func RemoveReference(ref types.ManagedObjectReference, refs []types.ManagedObjectReference) []types.ManagedObjectReference { + var result []types.ManagedObjectReference + + for i, r := range refs { + if r == ref { + result = append(result, refs[i+1:]...) + break + } + + result = append(result, r) + } + + return result +} + +// AddReference returns a slice with ref appended if not already in refs. +func AddReference(ref types.ManagedObjectReference, refs []types.ManagedObjectReference) []types.ManagedObjectReference { + if FindReference(refs, ref) == nil { + return append(refs, ref) + } + + return refs +} + +func (r *Registry) content() types.ServiceContent { + return r.Get(serviceInstance).(*ServiceInstance).Content +} + +// IsESX returns true if this Registry maps an ESX model +func (r *Registry) IsESX() bool { + return r.content().About.ApiType == "HostAgent" +} + +// IsVPX returns true if this Registry maps a VPX model +func (r *Registry) IsVPX() bool { + return !r.IsESX() +} + +// SearchIndex returns the SearchIndex singleton +func (r *Registry) SearchIndex() *SearchIndex { + return r.Get(r.content().SearchIndex.Reference()).(*SearchIndex) +} + +// FileManager returns the FileManager singleton +func (r *Registry) FileManager() *FileManager { + return r.Get(r.content().FileManager.Reference()).(*FileManager) +} + +// VirtualDiskManager returns the VirtualDiskManager singleton +func (r *Registry) VirtualDiskManager() *VirtualDiskManager { + return r.Get(r.content().VirtualDiskManager.Reference()).(*VirtualDiskManager) +} + +// ViewManager returns the ViewManager singleton +func (r *Registry) ViewManager() *ViewManager { + return r.Get(r.content().ViewManager.Reference()).(*ViewManager) +} diff --git a/vendor/github.com/vmware/govmomi/simulator/resource_pool.go b/vendor/github.com/vmware/govmomi/simulator/resource_pool.go new file mode 100644 index 00000000..ed8de91d --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/resource_pool.go @@ -0,0 +1,238 @@ +/* +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 simulator + +import ( + "strings" + + "github.com/vmware/govmomi/simulator/esx" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type ResourcePool struct { + mo.ResourcePool +} + +func NewResourcePool() *ResourcePool { + pool := &ResourcePool{ + ResourcePool: esx.ResourcePool, + } + + if Map.IsVPX() { + pool.DisabledMethod = nil // Enable VApp methods for VC + } + + return pool +} + +func NewResourceConfigSpec() types.ResourceConfigSpec { + spec := types.ResourceConfigSpec{ + CpuAllocation: new(types.ResourceAllocationInfo), + MemoryAllocation: new(types.ResourceAllocationInfo), + } + + return spec +} + +func (p *ResourcePool) setDefaultConfig(c types.BaseResourceAllocationInfo) { + info := c.GetResourceAllocationInfo() + + if info.Shares == nil { + info.Shares = new(types.SharesInfo) + } + + if info.Shares.Level == "" { + info.Shares.Level = types.SharesLevelNormal + } + + if info.ExpandableReservation == nil { + info.ExpandableReservation = types.NewBool(false) + } +} + +func (p *ResourcePool) createChild(name string, spec types.ResourceConfigSpec) (*ResourcePool, *soap.Fault) { + if e := Map.FindByName(name, p.ResourcePool.ResourcePool); e != nil { + return nil, Fault("", &types.DuplicateName{ + Name: e.Entity().Name, + Object: e.Reference(), + }) + } + + child := NewResourcePool() + + child.Name = name + child.Owner = p.Owner + child.Summary.GetResourcePoolSummary().Name = name + child.Config.CpuAllocation = spec.CpuAllocation + child.Config.MemoryAllocation = spec.MemoryAllocation + child.Config.Entity = spec.Entity + + p.setDefaultConfig(child.Config.CpuAllocation) + p.setDefaultConfig(child.Config.MemoryAllocation) + + return child, nil +} + +func (p *ResourcePool) CreateResourcePool(c *types.CreateResourcePool) soap.HasFault { + body := &methods.CreateResourcePoolBody{} + + child, err := p.createChild(c.Name, c.Spec) + if err != nil { + body.Fault_ = err + return body + } + + Map.PutEntity(p, Map.NewEntity(child)) + + p.ResourcePool.ResourcePool = append(p.ResourcePool.ResourcePool, child.Reference()) + + body.Res = &types.CreateResourcePoolResponse{ + Returnval: child.Reference(), + } + + return body +} + +type VirtualApp struct { + mo.VirtualApp +} + +func NewVAppConfigSpec() types.VAppConfigSpec { + spec := types.VAppConfigSpec{ + Annotation: "vcsim", + VmConfigSpec: types.VmConfigSpec{ + Product: []types.VAppProductSpec{ + { + Info: &types.VAppProductInfo{ + Name: "vcsim", + Vendor: "VMware", + VendorUrl: "http://www.vmware.com/", + Version: "0.1", + }, + ArrayUpdateSpec: types.ArrayUpdateSpec{ + Operation: types.ArrayUpdateOperationAdd, + }, + }, + }, + }, + } + + return spec +} + +func (p *ResourcePool) CreateVApp(req *types.CreateVApp) soap.HasFault { + body := &methods.CreateVAppBody{} + + pool, err := p.createChild(req.Name, req.ResSpec) + if err != nil { + body.Fault_ = err + return body + } + + child := &VirtualApp{} + child.ResourcePool = pool.ResourcePool + child.Self.Type = "VirtualApp" + child.ParentFolder = req.VmFolder + + if child.ParentFolder == nil { + folder := Map.getEntityDatacenter(p).VmFolder + child.ParentFolder = &folder + } + + child.VAppConfig = &types.VAppConfigInfo{ + VmConfigInfo: types.VmConfigInfo{}, + Annotation: req.ConfigSpec.Annotation, + } + + for _, product := range req.ConfigSpec.Product { + child.VAppConfig.Product = append(child.VAppConfig.Product, *product.Info) + } + + Map.PutEntity(p, Map.NewEntity(child)) + + p.ResourcePool.ResourcePool = append(p.ResourcePool.ResourcePool, child.Reference()) + + body.Res = &types.CreateVAppResponse{ + Returnval: child.Reference(), + } + + return body +} + +func (a *VirtualApp) CreateChildVMTask(req *types.CreateChildVM_Task) soap.HasFault { + body := &methods.CreateChildVM_TaskBody{} + + folder := Map.Get(*a.ParentFolder).(*Folder) + + res := folder.CreateVMTask(&types.CreateVM_Task{ + This: folder.Self, + Config: req.Config, + Host: req.Host, + Pool: req.This, + }) + + body.Res = &types.CreateChildVM_TaskResponse{ + Returnval: res.(*methods.CreateVM_TaskBody).Res.Returnval, + } + + return body +} + +func (a *VirtualApp) DestroyTask(req *types.Destroy_Task) soap.HasFault { + return (&ResourcePool{ResourcePool: a.ResourcePool}).DestroyTask(req) +} + +func (p *ResourcePool) DestroyTask(req *types.Destroy_Task) soap.HasFault { + task := CreateTask(p, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) { + if strings.HasSuffix(p.Parent.Type, "ComputeResource") { + // Can't destroy the root pool + return nil, &types.InvalidArgument{} + } + + pp := Map.Get(*p.Parent).(*ResourcePool) + + parent := &pp.ResourcePool + // Remove child reference from rp + parent.ResourcePool = RemoveReference(req.This, parent.ResourcePool) + + // The grandchildren become children of the parent (rp) + parent.ResourcePool = append(parent.ResourcePool, p.ResourcePool.ResourcePool...) + + // And VMs move to the parent + vms := p.ResourcePool.Vm + for _, vm := range vms { + Map.Get(vm).(*VirtualMachine).ResourcePool = &parent.Self + } + + parent.Vm = append(parent.Vm, vms...) + + Map.Remove(req.This) + + return nil, nil + }) + + task.Run() + + return &methods.Destroy_TaskBody{ + Res: &types.Destroy_TaskResponse{ + Returnval: task.Self, + }, + } +} diff --git a/vendor/github.com/vmware/govmomi/simulator/search_index.go b/vendor/github.com/vmware/govmomi/simulator/search_index.go new file mode 100644 index 00000000..c56dff2c --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/search_index.go @@ -0,0 +1,155 @@ +/* +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 simulator + +import ( + "strings" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type SearchIndex struct { + mo.SearchIndex +} + +func NewSearchIndex(ref types.ManagedObjectReference) object.Reference { + m := &SearchIndex{} + m.Self = ref + return m +} + +func (s *SearchIndex) FindByDatastorePath(r *types.FindByDatastorePath) soap.HasFault { + res := &methods.FindByDatastorePathBody{Res: new(types.FindByDatastorePathResponse)} + + for ref, obj := range Map.objects { + vm, ok := obj.(*VirtualMachine) + if !ok { + continue + } + + if vm.Config.Files.VmPathName == r.Path { + res.Res.Returnval = &ref + break + } + } + + return res +} + +func (s *SearchIndex) FindByInventoryPath(req *types.FindByInventoryPath) soap.HasFault { + body := &methods.FindByInventoryPathBody{Res: new(types.FindByInventoryPathResponse)} + + path := strings.Split(req.InventoryPath, "/") + if len(path) <= 1 { + return body + } + + root := Map.content().RootFolder + o := &root + + for _, name := range path[1:] { + f := s.FindChild(&types.FindChild{Entity: *o, Name: name}) + + o = f.(*methods.FindChildBody).Res.Returnval + if o == nil { + break + } + } + + body.Res.Returnval = o + + return body +} + +func (s *SearchIndex) FindChild(req *types.FindChild) soap.HasFault { + body := &methods.FindChildBody{} + + obj := Map.Get(req.Entity) + + if obj == nil { + body.Fault_ = Fault("", &types.ManagedObjectNotFound{Obj: req.Entity}) + return body + } + + body.Res = new(types.FindChildResponse) + + var children []types.ManagedObjectReference + + switch e := obj.(type) { + case *mo.Datacenter: + children = []types.ManagedObjectReference{e.VmFolder, e.HostFolder, e.DatastoreFolder, e.NetworkFolder} + case *Folder: + children = e.ChildEntity + case *mo.ComputeResource: + children = e.Host + children = append(children, *e.ResourcePool) + case *ClusterComputeResource: + children = e.Host + children = append(children, *e.ResourcePool) + case *ResourcePool: + children = e.ResourcePool.ResourcePool + children = append(children, e.Vm...) + case *VirtualApp: + children = e.ResourcePool.ResourcePool + children = append(children, e.Vm...) + } + + match := Map.FindByName(req.Name, children) + + if match != nil { + ref := match.Reference() + body.Res.Returnval = &ref + } + + return body +} + +func (s *SearchIndex) FindByUuid(req *types.FindByUuid) soap.HasFault { + body := &methods.FindByUuidBody{Res: new(types.FindByUuidResponse)} + + if req.VmSearch { + // Find Virtual Machine using UUID + for ref, obj := range Map.objects { + vm, ok := obj.(*VirtualMachine) + if !ok { + continue + } + if vm.Config.Uuid == req.Uuid { + body.Res.Returnval = &ref + break + } + } + } else { + // Find Host System using UUID + for ref, obj := range Map.objects { + host, ok := obj.(*HostSystem) + if !ok { + continue + } + if host.Summary.Hardware.Uuid == req.Uuid { + body.Res.Returnval = &ref + break + } + } + } + + return body +} diff --git a/vendor/github.com/vmware/govmomi/simulator/service_instance.go b/vendor/github.com/vmware/govmomi/simulator/service_instance.go new file mode 100644 index 00000000..ca46c490 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/service_instance.go @@ -0,0 +1,93 @@ +/* +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 simulator + +import ( + "time" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/simulator/vpx" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type ServiceInstance struct { + mo.ServiceInstance +} + +var serviceInstance = types.ManagedObjectReference{ + Type: "ServiceInstance", + Value: "ServiceInstance", +} + +func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *ServiceInstance { + Map = NewRegistry() + + s := &ServiceInstance{} + + s.Self = serviceInstance + s.Content = content + + Map.Put(s) + + f := &Folder{Folder: folder} + Map.Put(f) + + var setting []types.BaseOptionValue + + if content.About.ApiType == "HostAgent" { + CreateDefaultESX(f) + } else { + setting = vpx.Setting + } + + objects := []object.Reference{ + NewSessionManager(*s.Content.SessionManager), + NewPropertyCollector(s.Content.PropertyCollector), + NewFileManager(*s.Content.FileManager), + NewVirtualDiskManager(*s.Content.VirtualDiskManager), + NewLicenseManager(*s.Content.LicenseManager), + NewSearchIndex(*s.Content.SearchIndex), + NewViewManager(*s.Content.ViewManager), + NewTaskManager(*s.Content.TaskManager), + NewOptionManager(s.Content.Setting, setting), + } + + for _, o := range objects { + Map.Put(o) + } + + return s +} + +func (s *ServiceInstance) RetrieveServiceContent(*types.RetrieveServiceContent) soap.HasFault { + return &methods.RetrieveServiceContentBody{ + Res: &types.RetrieveServiceContentResponse{ + Returnval: s.Content, + }, + } +} + +func (*ServiceInstance) CurrentTime(*types.CurrentTime) soap.HasFault { + return &methods.CurrentTimeBody{ + Res: &types.CurrentTimeResponse{ + Returnval: time.Now(), + }, + } +} diff --git a/vendor/github.com/vmware/govmomi/simulator/session_manager.go b/vendor/github.com/vmware/govmomi/simulator/session_manager.go new file mode 100644 index 00000000..d05d5f5d --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/session_manager.go @@ -0,0 +1,83 @@ +/* +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 simulator + +import ( + "time" + + "github.com/google/uuid" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/session" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type SessionManager struct { + mo.SessionManager + + ServiceHostName string +} + +func NewSessionManager(ref types.ManagedObjectReference) object.Reference { + s := &SessionManager{} + s.Self = ref + return s +} + +func (s *SessionManager) Login(login *types.Login) soap.HasFault { + body := &methods.LoginBody{} + + if login.Locale == "" { + login.Locale = session.Locale + } + + if login.UserName == "" || login.Password == "" { + body.Fault_ = Fault("Login failure", &types.InvalidLogin{}) + } else { + body.Res = &types.LoginResponse{ + Returnval: types.UserSession{ + Key: uuid.New().String(), + UserName: login.UserName, + FullName: login.UserName, + LoginTime: time.Now(), + LastActiveTime: time.Now(), + Locale: login.Locale, + MessageLocale: login.Locale, + }, + } + } + + return body +} + +func (s *SessionManager) Logout(*types.Logout) soap.HasFault { + return &methods.LogoutBody{} +} + +func (s *SessionManager) AcquireGenericServiceTicket(ticket *types.AcquireGenericServiceTicket) soap.HasFault { + return &methods.AcquireGenericServiceTicketBody{ + Res: &types.AcquireGenericServiceTicketResponse{ + Returnval: types.SessionManagerGenericServiceTicket{ + Id: uuid.New().String(), + HostName: s.ServiceHostName, + }, + }, + } +} diff --git a/vendor/github.com/vmware/govmomi/simulator/simulator.go b/vendor/github.com/vmware/govmomi/simulator/simulator.go new file mode 100644 index 00000000..d8878098 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/simulator.go @@ -0,0 +1,526 @@ +/* +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 simulator + +import ( + "bytes" + "context" + "crypto/tls" + "crypto/x509" + "encoding/json" + "encoding/pem" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path" + "reflect" + "sort" + "strings" + + "github.com/vmware/govmomi/find" + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" + "github.com/vmware/govmomi/vim25/xml" +) + +// Trace when set to true, writes SOAP traffic to stderr +var Trace = false + +// Method encapsulates a decoded SOAP client request +type Method struct { + Name string + This types.ManagedObjectReference + Body types.AnyType +} + +// Service decodes incoming requests and dispatches to a Handler +type Service struct { + client *vim25.Client + + readAll func(io.Reader) ([]byte, error) + + TLS *tls.Config +} + +// Server provides a simulator Service over HTTP +type Server struct { + *httptest.Server + URL *url.URL + + caFile string +} + +// New returns an initialized simulator Service instance +func New(instance *ServiceInstance) *Service { + s := &Service{ + readAll: ioutil.ReadAll, + } + + s.client, _ = vim25.NewClient(context.Background(), s) + + return s +} + +type serverFaultBody struct { + Reason *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` +} + +func (b *serverFaultBody) Fault() *soap.Fault { return b.Reason } + +func serverFault(msg string) soap.HasFault { + return &serverFaultBody{Reason: Fault(msg, &types.InvalidRequest{})} +} + +// Fault wraps the given message and fault in a soap.Fault +func Fault(msg string, fault types.BaseMethodFault) *soap.Fault { + f := &soap.Fault{ + Code: "ServerFaultCode", + String: msg, + } + + f.Detail.Fault = fault + + return f +} + +func (s *Service) call(method *Method) soap.HasFault { + handler := Map.Get(method.This) + + if handler == nil { + msg := fmt.Sprintf("managed object not found: %s", method.This) + log.Print(msg) + fault := &types.ManagedObjectNotFound{Obj: method.This} + return &serverFaultBody{Reason: Fault(msg, fault)} + } + + name := method.Name + + if strings.HasSuffix(name, vTaskSuffix) { + // Make golint happy renaming "Foo_Task" -> "FooTask" + name = name[:len(name)-len(vTaskSuffix)] + sTaskSuffix + } + + m := reflect.ValueOf(handler).MethodByName(name) + if !m.IsValid() { + msg := fmt.Sprintf("%s does not implement: %s", method.This, method.Name) + log.Print(msg) + fault := &types.MethodNotFound{Receiver: method.This, Method: method.Name} + return &serverFaultBody{Reason: Fault(msg, fault)} + } + + if e, ok := handler.(mo.Entity); ok { + for _, dm := range e.Entity().DisabledMethod { + if name == dm { + msg := fmt.Sprintf("%s method is disabled: %s", method.This, method.Name) + fault := &types.MethodDisabled{} + return &serverFaultBody{Reason: Fault(msg, fault)} + } + } + } + + res := m.Call([]reflect.Value{reflect.ValueOf(method.Body)}) + + return res[0].Interface().(soap.HasFault) +} + +// RoundTrip implements the soap.RoundTripper interface in process. +// Rather than encode/decode SOAP over HTTP, this implementation uses reflection. +func (s *Service) RoundTrip(ctx context.Context, request, response soap.HasFault) error { + field := func(r soap.HasFault, name string) reflect.Value { + return reflect.ValueOf(r).Elem().FieldByName(name) + } + + // Every struct passed to soap.RoundTrip has "Req" and "Res" fields + req := field(request, "Req") + + // Every request has a "This" field. + this := req.Elem().FieldByName("This") + + method := &Method{ + Name: req.Elem().Type().Name(), + This: this.Interface().(types.ManagedObjectReference), + Body: req.Interface(), + } + + res := s.call(method) + + if err := res.Fault(); err != nil { + return soap.WrapSoapFault(err) + } + + field(response, "Res").Set(field(res, "Res")) + + return nil +} + +// soapEnvelope is a copy of soap.Envelope, with namespace changed to "soapenv", +// and additional namespace attributes required by some client libraries. +// Go still has issues decoding with such a namespace, but encoding is ok. +type soapEnvelope struct { + XMLName xml.Name `xml:"soapenv:Envelope"` + Enc string `xml:"xmlns:soapenc,attr"` + Env string `xml:"xmlns:soapenv,attr"` + XSD string `xml:"xmlns:xsd,attr"` + XSI string `xml:"xmlns:xsi,attr"` + Body interface{} `xml:"soapenv:Body"` +} + +// About generates some info about the simulator. +func (s *Service) About(w http.ResponseWriter, r *http.Request) { + var about struct { + Methods []string + Types []string + } + + seen := make(map[string]bool) + + f := reflect.TypeOf((*soap.HasFault)(nil)).Elem() + + for _, obj := range Map.objects { + kind := obj.Reference().Type + if seen[kind] { + continue + } + seen[kind] = true + + about.Types = append(about.Types, kind) + + t := reflect.TypeOf(obj) + for i := 0; i < t.NumMethod(); i++ { + m := t.Method(i) + if seen[m.Name] { + continue + } + seen[m.Name] = true + + if m.Type.NumIn() != 2 || m.Type.NumOut() != 1 || m.Type.Out(0) != f { + continue + } + + about.Methods = append(about.Methods, strings.Replace(m.Name, "Task", "_Task", 1)) + } + } + + sort.Strings(about.Methods) + sort.Strings(about.Types) + + w.Header().Set("Content-Type", "application/json") + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + _ = enc.Encode(&about) +} + +// ServeSDK implements the http.Handler interface +func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + body, err := s.readAll(r.Body) + _ = r.Body.Close() + if err != nil { + log.Printf("error reading body: %s", err) + w.WriteHeader(http.StatusBadRequest) + return + } + + if Trace { + fmt.Fprintf(os.Stderr, "Request: %s\n", string(body)) + } + + var res soap.HasFault + + method, err := UnmarshalBody(body) + if err != nil { + res = serverFault(err.Error()) + } else { + res = s.call(method) + } + + if res.Fault() == nil { + w.WriteHeader(http.StatusOK) + } else { + w.WriteHeader(http.StatusInternalServerError) + } + + var out bytes.Buffer + + fmt.Fprint(&out, xml.Header) + e := xml.NewEncoder(&out) + err = e.Encode(&soapEnvelope{ + Enc: "http://schemas.xmlsoap.org/soap/encoding/", + Env: "http://schemas.xmlsoap.org/soap/envelope/", + XSD: "http://www.w3.org/2001/XMLSchema", + XSI: "http://www.w3.org/2001/XMLSchema-instance", + Body: res, + }) + if err == nil { + err = e.Flush() + } + + if err != nil { + log.Printf("error encoding %s response: %s", method.Name, err) + return + } + + if Trace { + fmt.Fprintf(os.Stderr, "Response: %s\n", out.String()) + } + + _, _ = w.Write(out.Bytes()) +} + +func (s *Service) findDatastore(query url.Values) (*Datastore, error) { + ctx := context.Background() + + finder := find.NewFinder(s.client, false) + dc, err := finder.DatacenterOrDefault(ctx, query.Get("dcName")) + if err != nil { + return nil, err + } + + finder.SetDatacenter(dc) + + ds, err := finder.DatastoreOrDefault(ctx, query.Get("dsName")) + if err != nil { + return nil, err + } + + return Map.Get(ds.Reference()).(*Datastore), nil +} + +const folderPrefix = "/folder/" + +// ServeDatastore handler for Datastore access via /folder path. +func (s *Service) ServeDatastore(w http.ResponseWriter, r *http.Request) { + ds, ferr := s.findDatastore(r.URL.Query()) + if ferr != nil { + log.Printf("failed to locate datastore with query params: %s", r.URL.RawQuery) + w.WriteHeader(http.StatusNotFound) + return + } + + file := strings.TrimPrefix(r.URL.Path, folderPrefix) + p := path.Join(ds.Info.GetDatastoreInfo().Url, file) + + switch r.Method { + case "GET": + f, err := os.Open(p) + if err != nil { + log.Printf("failed to %s '%s': %s", r.Method, p, err) + w.WriteHeader(http.StatusNotFound) + return + } + defer f.Close() + + _, _ = io.Copy(w, f) + case "POST": + _, err := os.Stat(p) + if err == nil { + // File exists + w.WriteHeader(http.StatusConflict) + return + } + + // File does not exist, fallthrough to create via PUT logic + fallthrough + case "PUT": + f, err := os.Create(p) + if err != nil { + log.Printf("failed to %s '%s': %s", r.Method, p, err) + w.WriteHeader(http.StatusInternalServerError) + return + } + defer f.Close() + + _, _ = io.Copy(f, r.Body) + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} + +// ServiceVersions handler for the /sdk/vimServiceVersions.xml path. +func (*Service) ServiceVersions(w http.ResponseWriter, r *http.Request) { + // pyvmomi depends on this + + const versions = xml.Header + ` + + urn:vim25 + 6.5 + + 6.0 + 5.5 + + + +` + fmt.Fprint(w, versions) +} + +// NewServer returns an http Server instance for the given service +func (s *Service) NewServer() *Server { + mux := http.NewServeMux() + path := "/sdk" + + mux.HandleFunc(path, s.ServeSDK) + mux.HandleFunc(path+"/vimServiceVersions.xml", s.ServiceVersions) + mux.HandleFunc(folderPrefix, s.ServeDatastore) + mux.HandleFunc("/about", s.About) + + // Using NewUnstartedServer() instead of NewServer(), + // for use in main.go, where Start() blocks, we can still set ServiceHostName + ts := httptest.NewUnstartedServer(mux) + + u := &url.URL{ + Scheme: "http", + Host: ts.Listener.Addr().String(), + Path: path, + User: url.UserPassword("user", "pass"), + } + + // Redirect clients to this http server, rather than HostSystem.Name + Map.Get(*s.client.ServiceContent.SessionManager).(*SessionManager).ServiceHostName = u.Host + + if f := flag.Lookup("httptest.serve"); f != nil { + // Avoid the blocking behaviour of httptest.Server.Start() when this flag is set + _ = f.Value.Set("") + } + + if s.TLS == nil { + ts.Start() + } else { + ts.TLS = s.TLS + ts.StartTLS() + u.Scheme += "s" + } + + return &Server{ + Server: ts, + URL: u, + } +} + +// Certificate returns the TLS certificate for the Server if started with TLS enabled. +// This method will panic if TLS is not enabled for the server. +func (s *Server) Certificate() *x509.Certificate { + // By default httptest.StartTLS uses http/internal.LocalhostCert, which we can access here: + cert, _ := x509.ParseCertificate(s.TLS.Certificates[0].Certificate[0]) + return cert +} + +// CertificateInfo returns Server.Certificate() as object.HostCertificateInfo +func (s *Server) CertificateInfo() *object.HostCertificateInfo { + info := new(object.HostCertificateInfo) + info.FromCertificate(s.Certificate()) + return info +} + +// CertificateFile returns a file name, where the file contains the PEM encoded Server.Certificate. +// The temporary file is removed when Server.Close() is called. +func (s *Server) CertificateFile() (string, error) { + if s.caFile != "" { + return s.caFile, nil + } + + f, err := ioutil.TempFile("", "vcsim-") + if err != nil { + return "", err + } + defer f.Close() + + s.caFile = f.Name() + cert := s.Certificate() + return s.caFile, pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) +} + +// Close shuts down the server and blocks until all outstanding +// requests on this server have completed. +func (s *Server) Close() { + s.Server.Close() + if s.caFile != "" { + _ = os.Remove(s.caFile) + } +} + +var typeFunc = types.TypeFunc() + +// UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type +func UnmarshalBody(data []byte) (*Method, error) { + body := struct { + Content string `xml:",innerxml"` + }{} + + req := soap.Envelope{ + Body: &body, + } + + err := xml.Unmarshal(data, &req) + if err != nil { + return nil, fmt.Errorf("xml.Unmarshal: %s", err) + } + + decoder := xml.NewDecoder(bytes.NewReader([]byte(body.Content))) + decoder.TypeFunc = typeFunc // required to decode interface types + + var start *xml.StartElement + + for { + tok, derr := decoder.Token() + if derr != nil { + return nil, fmt.Errorf("decoding body: %s", err) + } + if t, ok := tok.(xml.StartElement); ok { + start = &t + break + } + } + + kind := start.Name.Local + + rtype, ok := typeFunc(kind) + if !ok { + return nil, fmt.Errorf("no vmomi type defined for '%s'", kind) + } + + var val interface{} + if rtype != nil { + val = reflect.New(rtype).Interface() + } + + err = decoder.DecodeElement(val, start) + if err != nil { + return nil, fmt.Errorf("decoding %s: %s", kind, err) + } + + method := &Method{Name: kind, Body: val} + + field := reflect.ValueOf(val).Elem().FieldByName("This") + + method.This = field.Interface().(types.ManagedObjectReference) + + return method, nil +} diff --git a/vendor/github.com/vmware/govmomi/simulator/task.go b/vendor/github.com/vmware/govmomi/simulator/task.go new file mode 100644 index 00000000..7ef17498 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/task.go @@ -0,0 +1,100 @@ +/* +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 simulator + +import ( + "fmt" + "reflect" + "strings" + "time" + + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +const vTaskSuffix = "_Task" // vmomi suffix +const sTaskSuffix = "Task" // simulator suffix (avoiding golint warning) + +type Task struct { + mo.Task + + Execute func(*Task) (types.AnyType, types.BaseMethodFault) +} + +func NewTask(runner TaskRunner) *Task { + ref := runner.Reference() + name := reflect.TypeOf(runner).Elem().Name() + name = strings.Replace(name, "VM", "Vm", 1) // "VM" for the type to make go-lint happy, but "Vm" for the vmodl ID + return CreateTask(ref, name, runner.Run) +} + +func CreateTask(e mo.Reference, name string, run func(*Task) (types.AnyType, types.BaseMethodFault)) *Task { + ref := e.Reference() + id := name + + if strings.HasSuffix(id, sTaskSuffix) { + id = id[:len(id)-len(sTaskSuffix)] + name = id + vTaskSuffix + } + + task := &Task{ + Execute: run, + } + + Map.Put(task) + + task.Info.Key = task.Self.Value + task.Info.Task = task.Self + task.Info.Name = ucFirst(name) + task.Info.DescriptionId = fmt.Sprintf("%s.%s", ref.Type, id) + task.Info.Entity = &ref + task.Info.EntityName = ref.Value + + task.Info.QueueTime = time.Now() + task.Info.State = types.TaskInfoStateQueued + + return task +} + +type TaskRunner interface { + mo.Reference + + Run(*Task) (types.AnyType, types.BaseMethodFault) +} + +func (t *Task) Run() { + now := time.Now() + t.Info.StartTime = &now + + t.Info.State = types.TaskInfoStateRunning + + res, err := t.Execute(t) + + now = time.Now() + t.Info.CompleteTime = &now + + if err != nil { + t.Info.State = types.TaskInfoStateError + t.Info.Error = &types.LocalizedMethodFault{ + Fault: err, + LocalizedMessage: fmt.Sprintf("%T", err), + } + } else { + t.Info.Result = res + t.Info.State = types.TaskInfoStateSuccess + } +} diff --git a/vendor/github.com/vmware/govmomi/simulator/task_manager.go b/vendor/github.com/vmware/govmomi/simulator/task_manager.go new file mode 100644 index 00000000..df271082 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/task_manager.go @@ -0,0 +1,52 @@ +/* +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 simulator + +import ( + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +var recentTaskMax = 200 // the VC limit + +type TaskManager struct { + mo.TaskManager +} + +func NewTaskManager(ref types.ManagedObjectReference) object.Reference { + s := &TaskManager{} + s.Self = ref + Map.AddHandler(s) + return s +} + +func (m *TaskManager) PutObject(obj mo.Reference) { + ref := obj.Reference() + if ref.Type != "Task" { + return + } + + m.RecentTask = append(m.RecentTask, ref) + + if len(m.RecentTask) > recentTaskMax { + m.RecentTask = m.RecentTask[1:] + } +} + +func (m *TaskManager) RemoveObject(_ types.ManagedObjectReference) { +} diff --git a/vendor/github.com/vmware/govmomi/simulator/view_manager.go b/vendor/github.com/vmware/govmomi/simulator/view_manager.go new file mode 100644 index 00000000..959f2284 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/view_manager.go @@ -0,0 +1,184 @@ +/* +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 simulator + +import ( + "reflect" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type ViewManager struct { + mo.ViewManager + + entities map[string]bool +} + +var entities = []struct { + Type reflect.Type + Container bool +}{ + {reflect.TypeOf((*mo.ManagedEntity)(nil)).Elem(), true}, + {reflect.TypeOf((*mo.Folder)(nil)).Elem(), true}, + {reflect.TypeOf((*mo.StoragePod)(nil)).Elem(), true}, + {reflect.TypeOf((*mo.Datacenter)(nil)).Elem(), true}, + {reflect.TypeOf((*mo.ComputeResource)(nil)).Elem(), true}, + {reflect.TypeOf((*mo.ClusterComputeResource)(nil)).Elem(), true}, + {reflect.TypeOf((*mo.HostSystem)(nil)).Elem(), true}, + {reflect.TypeOf((*mo.ResourcePool)(nil)).Elem(), true}, + {reflect.TypeOf((*mo.VirtualApp)(nil)).Elem(), true}, + {reflect.TypeOf((*mo.VirtualMachine)(nil)).Elem(), false}, + {reflect.TypeOf((*mo.Datastore)(nil)).Elem(), false}, + {reflect.TypeOf((*mo.Network)(nil)).Elem(), false}, + {reflect.TypeOf((*mo.OpaqueNetwork)(nil)).Elem(), false}, + {reflect.TypeOf((*mo.DistributedVirtualPortgroup)(nil)).Elem(), false}, + {reflect.TypeOf((*mo.DistributedVirtualSwitch)(nil)).Elem(), false}, + {reflect.TypeOf((*mo.VmwareDistributedVirtualSwitch)(nil)).Elem(), false}, +} + +func NewViewManager(ref types.ManagedObjectReference) object.Reference { + s := &ViewManager{ + entities: make(map[string]bool), + } + + s.Self = ref + + for _, e := range entities { + s.entities[e.Type.Name()] = e.Container + } + + return s +} + +func destroyView(ref types.ManagedObjectReference) soap.HasFault { + m := Map.ViewManager() + + m.ViewList = RemoveReference(ref, m.ViewList) + + return &methods.DestroyViewBody{ + Res: &types.DestroyViewResponse{}, + } +} + +func (m *ViewManager) CreateContainerView(req *types.CreateContainerView) soap.HasFault { + body := &methods.CreateContainerViewBody{} + + root := Map.Get(req.Container) + if root == nil { + body.Fault_ = Fault("", &types.ManagedObjectNotFound{Obj: req.Container}) + return body + } + + if m.entities[root.Reference().Type] != true { + body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "container"}) + return body + } + + container := &ContainerView{ + mo.ContainerView{ + Container: root.Reference(), + Recursive: req.Recursive, + Type: req.Type, + }, + make(map[string]bool), + } + + for _, ctype := range container.Type { + if _, ok := m.entities[ctype]; !ok { + body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "type"}) + return body + } + + container.types[ctype] = true + + for _, e := range entities { + // Check for embedded types + if f, ok := e.Type.FieldByName(ctype); ok && f.Anonymous { + container.types[e.Type.Name()] = true + } + } + } + + Map.Put(container) + + m.ViewList = append(m.ViewList, container.Reference()) + + body.Res = &types.CreateContainerViewResponse{ + Returnval: container.Self, + } + + container.add(root) + + return body +} + +type ContainerView struct { + mo.ContainerView + + types map[string]bool +} + +func (v *ContainerView) DestroyView(c *types.DestroyView) soap.HasFault { + return destroyView(c.This) +} + +func (v *ContainerView) include(o types.ManagedObjectReference) bool { + if len(v.types) == 0 { + return true + } + + return v.types[o.Type] +} + +func (v *ContainerView) add(root mo.Reference) { + var children []types.ManagedObjectReference + + switch e := root.(type) { + case *mo.Datacenter: + children = []types.ManagedObjectReference{e.VmFolder, e.HostFolder, e.DatastoreFolder, e.NetworkFolder} + case *Folder: + children = e.ChildEntity + case *mo.ComputeResource: + children = e.Host + children = append(children, *e.ResourcePool) + case *ClusterComputeResource: + children = e.Host + children = append(children, *e.ResourcePool) + case *ResourcePool: + children = e.ResourcePool.ResourcePool + children = append(children, e.Vm...) + case *VirtualApp: + children = e.ResourcePool.ResourcePool + children = append(children, e.Vm...) + case *HostSystem: + children = e.Vm + } + + for _, child := range children { + if v.include(child) { + v.View = AddReference(child, v.View) + } + + if v.Recursive { + v.add(Map.Get(child)) + } + } +} diff --git a/vendor/github.com/vmware/govmomi/simulator/virtual_disk_manager.go b/vendor/github.com/vmware/govmomi/simulator/virtual_disk_manager.go new file mode 100644 index 00000000..622d272e --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/virtual_disk_manager.go @@ -0,0 +1,175 @@ +/* +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 simulator + +import ( + "os" + "strings" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type VirtualDiskManager struct { + mo.VirtualDiskManager +} + +func NewVirtualDiskManager(ref types.ManagedObjectReference) object.Reference { + m := &VirtualDiskManager{} + m.Self = ref + return m +} + +func (m *VirtualDiskManager) names(name string) []string { + return []string{ + strings.Replace(name, ".vmdk", "-flat.vmdk", 1), + name, + } +} + +func (m *VirtualDiskManager) createVirtualDisk(req *types.CreateVirtualDisk_Task) types.BaseMethodFault { + fm := Map.FileManager() + + file, fault := fm.resolve(req.Datacenter, req.Name) + if fault != nil { + return fault + } + + for _, name := range m.names(file) { + _, err := os.Stat(name) + if err == nil { + return fm.fault(name, nil, new(types.FileAlreadyExists)) + } + + f, err := os.Create(name) + if err != nil { + return fm.fault(name, err, new(types.CannotCreateFile)) + } + + _ = f.Close() + } + + return nil +} + +func (m *VirtualDiskManager) CreateVirtualDiskTask(req *types.CreateVirtualDisk_Task) soap.HasFault { + task := CreateTask(m, "createVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) { + return nil, m.createVirtualDisk(req) + }) + + task.Run() + + return &methods.CreateVirtualDisk_TaskBody{ + Res: &types.CreateVirtualDisk_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (m *VirtualDiskManager) DeleteVirtualDiskTask(req *types.DeleteVirtualDisk_Task) soap.HasFault { + task := CreateTask(m, "deleteVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) { + fm := Map.FileManager() + + for _, name := range m.names(req.Name) { + err := fm.deleteDatastoreFile(&types.DeleteDatastoreFile_Task{ + Name: name, + Datacenter: req.Datacenter, + }) + + if err != nil { + return nil, err + } + } + + return nil, nil + }) + + task.Run() + + return &methods.DeleteVirtualDisk_TaskBody{ + Res: &types.DeleteVirtualDisk_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (m *VirtualDiskManager) MoveVirtualDiskTask(req *types.MoveVirtualDisk_Task) soap.HasFault { + task := CreateTask(m, "moveVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) { + fm := Map.FileManager() + + dest := m.names(req.DestName) + + for i, name := range m.names(req.SourceName) { + err := fm.moveDatastoreFile(&types.MoveDatastoreFile_Task{ + SourceName: name, + SourceDatacenter: req.SourceDatacenter, + DestinationName: dest[i], + DestinationDatacenter: req.DestDatacenter, + Force: req.Force, + }) + + if err != nil { + return nil, err + } + } + + return nil, nil + }) + + task.Run() + + return &methods.MoveVirtualDisk_TaskBody{ + Res: &types.MoveVirtualDisk_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (m *VirtualDiskManager) CopyVirtualDiskTask(req *types.CopyVirtualDisk_Task) soap.HasFault { + task := CreateTask(m, "copyVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) { + fm := Map.FileManager() + + dest := m.names(req.DestName) + + for i, name := range m.names(req.SourceName) { + err := fm.copyDatastoreFile(&types.CopyDatastoreFile_Task{ + SourceName: name, + SourceDatacenter: req.SourceDatacenter, + DestinationName: dest[i], + DestinationDatacenter: req.DestDatacenter, + Force: req.Force, + }) + + if err != nil { + return nil, err + } + } + + return nil, nil + }) + + task.Run() + + return &methods.CopyVirtualDisk_TaskBody{ + Res: &types.CopyVirtualDisk_TaskResponse{ + Returnval: task.Self, + }, + } +} diff --git a/vendor/github.com/vmware/govmomi/simulator/virtual_machine.go b/vendor/github.com/vmware/govmomi/simulator/virtual_machine.go new file mode 100644 index 00000000..dd3e2c75 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/virtual_machine.go @@ -0,0 +1,641 @@ +/* +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 simulator + +import ( + "fmt" + "io" + "log" + "net" + "os" + "path" + "strings" + "time" + + "github.com/google/uuid" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/simulator/esx" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +type VirtualMachine struct { + mo.VirtualMachine + + log *log.Logger + out io.Closer +} + +func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualMachineConfigSpec) (*VirtualMachine, types.BaseMethodFault) { + vm := &VirtualMachine{} + vm.Parent = &parent + + if spec.Name == "" { + return nil, &types.InvalidVmConfig{Property: "configSpec.name"} + } + + if spec.Files == nil || spec.Files.VmPathName == "" { + return nil, &types.InvalidVmConfig{Property: "configSpec.files.vmPathName"} + } + + vm.Config = &types.VirtualMachineConfigInfo{ + ExtraConfig: []types.BaseOptionValue{&types.OptionValue{Key: "govcsim", Value: "TRUE"}}, + } + vm.Summary.Guest = &types.VirtualMachineGuestSummary{} + vm.Summary.Storage = &types.VirtualMachineStorageSummary{} + + // Append VM Name as the directory name if not specified + if strings.HasSuffix(spec.Files.VmPathName, "]") { // e.g. "[datastore1]" + spec.Files.VmPathName += " " + spec.Name + } + + if !strings.HasSuffix(spec.Files.VmPathName, ".vmx") { + spec.Files.VmPathName = path.Join(spec.Files.VmPathName, spec.Name+".vmx") + } + + dsPath := path.Dir(spec.Files.VmPathName) + + defaults := types.VirtualMachineConfigSpec{ + NumCPUs: 1, + NumCoresPerSocket: 1, + MemoryMB: 32, + Uuid: uuid.New().String(), + Version: "vmx-11", + Files: &types.VirtualMachineFileInfo{ + SnapshotDirectory: dsPath, + SuspendDirectory: dsPath, + LogDirectory: dsPath, + }, + } + + // Add the default devices + defaults.DeviceChange, _ = object.VirtualDeviceList(esx.VirtualDevice).ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd) + + err := vm.configure(&defaults) + if err != nil { + return nil, err + } + + vm.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff + vm.Runtime.ConnectionState = types.VirtualMachineConnectionStateConnected + vm.Summary.Runtime = vm.Runtime + + vm.Summary.QuickStats.GuestHeartbeatStatus = types.ManagedEntityStatusGray + vm.Summary.OverallStatus = types.ManagedEntityStatusGreen + vm.ConfigStatus = types.ManagedEntityStatusGreen + + return vm, nil +} + +func (vm *VirtualMachine) apply(spec *types.VirtualMachineConfigSpec) { + if spec.Files == nil { + spec.Files = new(types.VirtualMachineFileInfo) + } + + apply := []struct { + src string + dst *string + }{ + {spec.Name, &vm.Name}, + {spec.Name, &vm.Config.Name}, + {spec.Name, &vm.Summary.Config.Name}, + {spec.GuestId, &vm.Config.GuestId}, + {spec.GuestId, &vm.Config.GuestFullName}, + {spec.GuestId, &vm.Summary.Guest.GuestId}, + {spec.GuestId, &vm.Summary.Config.GuestId}, + {spec.GuestId, &vm.Summary.Config.GuestFullName}, + {spec.Uuid, &vm.Config.Uuid}, + {spec.Version, &vm.Config.Version}, + {spec.Files.VmPathName, &vm.Config.Files.VmPathName}, + {spec.Files.VmPathName, &vm.Summary.Config.VmPathName}, + {spec.Files.SnapshotDirectory, &vm.Config.Files.SnapshotDirectory}, + {spec.Files.LogDirectory, &vm.Config.Files.LogDirectory}, + } + + for _, f := range apply { + if f.src != "" { + *f.dst = f.src + } + } + + if spec.MemoryMB != 0 { + vm.Config.Hardware.MemoryMB = int32(spec.MemoryMB) + vm.Summary.Config.MemorySizeMB = vm.Config.Hardware.MemoryMB + } + + if spec.NumCPUs != 0 { + vm.Config.Hardware.NumCPU = spec.NumCPUs + vm.Summary.Config.NumCpu = vm.Config.Hardware.NumCPU + } + + vm.Config.ExtraConfig = append(vm.Config.ExtraConfig, spec.ExtraConfig...) + + vm.Config.Modified = time.Now() + + vm.Summary.Config.Uuid = vm.Config.Uuid +} + +func (vm *VirtualMachine) configure(spec *types.VirtualMachineConfigSpec) types.BaseMethodFault { + vm.apply(spec) + + return vm.configureDevices(spec) +} + +func (vm *VirtualMachine) useDatastore(name string) *Datastore { + host := Map.Get(*vm.Runtime.Host).(*HostSystem) + + ds := Map.FindByName(name, host.Datastore).(*Datastore) + + vm.Datastore = AddReference(ds.Self, vm.Datastore) + + return ds +} + +func (vm *VirtualMachine) setLog(w io.WriteCloser) { + vm.out = w + vm.log = log.New(w, "vmx ", log.Flags()) +} + +func (vm *VirtualMachine) createFile(spec string, name string, register bool) (*os.File, types.BaseMethodFault) { + p, fault := parseDatastorePath(spec) + if fault != nil { + return nil, fault + } + + ds := vm.useDatastore(p.Datastore) + + file := path.Join(ds.Info.GetDatastoreInfo().Url, p.Path) + + if name != "" { + if path.Ext(file) != "" { + file = path.Dir(file) + } + + file = path.Join(file, name) + } + + if register { + f, err := os.Open(file) + if err != nil { + log.Printf("register %s: %s", vm.Reference(), err) + if os.IsNotExist(err) { + return nil, &types.NotFound{} + } + + return nil, &types.InvalidArgument{} + } + + return f, nil + } + + dir := path.Dir(file) + + _ = os.MkdirAll(dir, 0700) + + _, err := os.Stat(file) + if err == nil { + return nil, &types.FileAlreadyExists{ + FileFault: types.FileFault{ + File: file, + }, + } + } + + f, err := os.Create(file) + if err != nil { + return nil, &types.FileFault{ + File: file, + } + } + + return f, nil +} + +func (vm *VirtualMachine) create(spec *types.VirtualMachineConfigSpec, register bool) types.BaseMethodFault { + vm.apply(spec) + + files := []struct { + spec string + name string + use func(w io.WriteCloser) + }{ + {vm.Config.Files.VmPathName, "", nil}, + {vm.Config.Files.VmPathName, fmt.Sprintf("%s.nvram", vm.Name), nil}, + {vm.Config.Files.LogDirectory, "vmware.log", vm.setLog}, + } + + for _, file := range files { + f, err := vm.createFile(file.spec, file.name, register) + if err != nil { + return err + } + + if file.use != nil { + file.use(f) + } else { + _ = f.Close() + } + } + + vm.log.Print("created") + + return vm.configureDevices(spec) +} + +var vmwOUI = net.HardwareAddr([]byte{0x0, 0xc, 0x29}) + +// From http://pubs.vmware.com/vsphere-60/index.jsp?topic=%2Fcom.vmware.vsphere.networking.doc%2FGUID-DC7478FF-DC44-4625-9AD7-38208C56A552.html +// "The host generates generateMAC addresses that consists of the VMware OUI 00:0C:29 and the last three octets in hexadecimal +// format of the virtual machine UUID. The virtual machine UUID is based on a hash calculated by using the UUID of the +// ESXi physical machine and the path to the configuration file (.vmx) of the virtual machine." +func (vm *VirtualMachine) generateMAC() string { + id := uuid.New() // Random is fine for now. + + offset := len(id) - len(vmwOUI) + + mac := append(vmwOUI, id[offset:]...) + + return mac.String() +} + +func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, device types.BaseVirtualDevice) types.BaseMethodFault { + d := device.GetVirtualDevice() + var controller types.BaseVirtualController + + if d.Key < 0 { + // Choose a unique key + if d.Key == -1 { + d.Key = devices.NewKey() + } + + d.Key *= -1 + + for { + if devices.FindByKey(d.Key) == nil { + break + } + d.Key++ + } + } + + label := devices.Name(device) + summary := label + dc := Map.getEntityDatacenter(Map.Get(*vm.Parent).(mo.Entity)) + dm := Map.VirtualDiskManager() + + switch x := device.(type) { + case types.BaseVirtualEthernetCard: + controller = devices.PickController((*types.VirtualPCIController)(nil)) + var net types.ManagedObjectReference + + switch b := d.Backing.(type) { + case *types.VirtualEthernetCardNetworkBackingInfo: + summary = b.DeviceName + net = Map.FindByName(b.DeviceName, dc.Network).Reference() + b.Network = &net + case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo: + summary = fmt.Sprintf("DVSwitch: %s", b.Port.SwitchUuid) + net.Type = "DistributedVirtualPortgroup" + net.Value = b.Port.PortgroupKey + } + + vm.Network = append(vm.Network, net) + + c := x.GetVirtualEthernetCard() + if c.MacAddress == "" { + c.MacAddress = vm.generateMAC() + } + case *types.VirtualDisk: + switch b := d.Backing.(type) { + case types.BaseVirtualDeviceFileBackingInfo: + err := dm.createVirtualDisk(&types.CreateVirtualDisk_Task{ + Datacenter: &dc.Self, + Name: b.GetVirtualDeviceFileBackingInfo().FileName, + }) + + if err != nil { + return err + } + } + } + + if d.UnitNumber == nil && controller != nil { + devices.AssignController(device, controller) + } + + if d.DeviceInfo == nil { + d.DeviceInfo = &types.Description{ + Label: label, + Summary: summary, + } + } + + return nil +} + +func removeDevice(devices object.VirtualDeviceList, device types.BaseVirtualDevice) object.VirtualDeviceList { + var result object.VirtualDeviceList + + for i, d := range devices { + if d.GetVirtualDevice().Key == device.GetVirtualDevice().Key { + result = append(result, devices[i+1:]...) + break + } + + result = append(result, d) + } + + return result +} + +func (vm *VirtualMachine) configureDevices(spec *types.VirtualMachineConfigSpec) types.BaseMethodFault { + devices := object.VirtualDeviceList(vm.Config.Hardware.Device) + + for i, change := range spec.DeviceChange { + dspec := change.GetVirtualDeviceConfigSpec() + device := dspec.Device.GetVirtualDevice() + invalid := &types.InvalidDeviceSpec{DeviceIndex: int32(i)} + + switch dspec.Operation { + case types.VirtualDeviceConfigSpecOperationAdd: + if devices.FindByKey(device.Key) != nil { + if vm.Self.Value != "" { // moid isn't set until CreateVM is done + return invalid + } + + // In this case, the CreateVM() spec included one of the default devices + devices = removeDevice(devices, device) + } + + err := vm.configureDevice(devices, dspec.Device) + if err != nil { + return err + } + + devices = append(devices, dspec.Device) + case types.VirtualDeviceConfigSpecOperationRemove: + devices = removeDevice(devices, dspec.Device) + } + } + + vm.Config.Hardware.Device = []types.BaseVirtualDevice(devices) + + return nil +} + +type powerVMTask struct { + *VirtualMachine + + state types.VirtualMachinePowerState +} + +func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) { + c.log.Printf("running power task: requesting %s, existing %s", + c.state, c.VirtualMachine.Runtime.PowerState) + + if c.VirtualMachine.Runtime.PowerState == c.state { + return nil, &types.InvalidPowerState{ + RequestedState: c.state, + ExistingState: c.VirtualMachine.Runtime.PowerState, + } + } + + c.VirtualMachine.Runtime.PowerState = c.state + c.VirtualMachine.Summary.Runtime.PowerState = c.state + + bt := &c.VirtualMachine.Summary.Runtime.BootTime + if c.state == types.VirtualMachinePowerStatePoweredOn { + now := time.Now() + *bt = &now + } else { + *bt = nil + } + + return nil, nil +} + +func (vm *VirtualMachine) PowerOnVMTask(c *types.PowerOnVM_Task) soap.HasFault { + r := &methods.PowerOnVM_TaskBody{} + + runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOn} + task := CreateTask(runner.Reference(), "powerOn", runner.Run) + + r.Res = &types.PowerOnVM_TaskResponse{ + Returnval: task.Self, + } + + task.Run() + + return r +} + +func (vm *VirtualMachine) PowerOffVMTask(c *types.PowerOffVM_Task) soap.HasFault { + r := &methods.PowerOffVM_TaskBody{} + + runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOff} + task := CreateTask(runner.Reference(), "powerOff", runner.Run) + + r.Res = &types.PowerOffVM_TaskResponse{ + Returnval: task.Self, + } + + task.Run() + + return r +} + +func (vm *VirtualMachine) ReconfigVMTask(req *types.ReconfigVM_Task) soap.HasFault { + task := CreateTask(vm, "reconfigVm", func(t *Task) (types.AnyType, types.BaseMethodFault) { + err := vm.configure(&req.Spec) + if err != nil { + return nil, err + } + + return nil, nil + }) + + task.Run() + + return &methods.ReconfigVM_TaskBody{ + Res: &types.ReconfigVM_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (vm *VirtualMachine) DestroyTask(req *types.Destroy_Task) soap.HasFault { + task := CreateTask(vm, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) { + r := vm.UnregisterVM(&types.UnregisterVM{ + This: req.This, + }) + + if r.Fault() != nil { + return nil, r.Fault().VimFault().(types.BaseMethodFault) + } + + // Delete VM files from the datastore (ignoring result for now) + m := Map.FileManager() + dc := Map.getEntityDatacenter(vm).Reference() + + _ = m.DeleteDatastoreFileTask(&types.DeleteDatastoreFile_Task{ + This: m.Reference(), + Name: vm.Config.Files.LogDirectory, + Datacenter: &dc, + }) + + return nil, nil + }) + + task.Run() + + return &methods.Destroy_TaskBody{ + Res: &types.Destroy_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (vm *VirtualMachine) UnregisterVM(c *types.UnregisterVM) soap.HasFault { + r := &methods.UnregisterVMBody{} + + if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn { + r.Fault_ = Fault("", &types.InvalidPowerState{ + RequestedState: types.VirtualMachinePowerStatePoweredOff, + ExistingState: vm.Runtime.PowerState, + }) + + return r + } + + _ = vm.out.Close() // Close log fd + + Map.getEntityParent(vm, "Folder").(*Folder).removeChild(c.This) + + host := Map.Get(*vm.Runtime.Host).(*HostSystem) + host.Vm = RemoveReference(vm.Self, host.Vm) + + switch pool := Map.Get(*vm.ResourcePool).(type) { + case *ResourcePool: + pool.Vm = RemoveReference(vm.Self, pool.Vm) + case *VirtualApp: + pool.Vm = RemoveReference(vm.Self, pool.Vm) + } + + for i := range vm.Datastore { + ds := Map.Get(vm.Datastore[i]).(*Datastore) + ds.Vm = RemoveReference(vm.Self, ds.Vm) + } + + r.Res = new(types.UnregisterVMResponse) + + return r +} + +func (vm *VirtualMachine) CloneVMTask(req *types.CloneVM_Task) soap.HasFault { + task := CreateTask(vm, "cloneVm", func(t *Task) (types.AnyType, types.BaseMethodFault) { + folder := Map.Get(req.Folder).(*Folder) + + config := types.VirtualMachineConfigSpec{ + Name: req.Name, + GuestId: vm.Config.GuestId, + Files: &types.VirtualMachineFileInfo{ + VmPathName: strings.Replace(vm.Config.Files.VmPathName, vm.Name, req.Name, -1), + }, + } + + res := folder.CreateVMTask(&types.CreateVM_Task{ + This: folder.Self, + Config: config, + Pool: *vm.ResourcePool, + }) + + ctask := Map.Get(res.(*methods.CreateVM_TaskBody).Res.Returnval).(*Task) + if ctask.Info.Error != nil { + return nil, ctask.Info.Error.Fault + } + + return ctask.Info.Result.(types.ManagedObjectReference), nil + }) + + task.Run() + + return &methods.CloneVM_TaskBody{ + Res: &types.CloneVM_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (vm *VirtualMachine) RelocateVMTask(req *types.RelocateVM_Task) soap.HasFault { + task := CreateTask(vm, "relocateVm", func(t *Task) (types.AnyType, types.BaseMethodFault) { + if ref := req.Spec.Datastore; ref != nil { + ds := Map.Get(*ref).(*Datastore) + ds.Vm = RemoveReference(*ref, ds.Vm) + + vm.Datastore = []types.ManagedObjectReference{*ref} + + // TODO: migrate vm.Config.Files (and vm.Summary.Config.VmPathName) + } + + if ref := req.Spec.Pool; ref != nil { + pool := Map.Get(*ref).(*ResourcePool) + pool.Vm = RemoveReference(*ref, pool.Vm) + + vm.ResourcePool = ref + } + + if ref := req.Spec.Host; ref != nil { + host := Map.Get(*ref).(*HostSystem) + host.Vm = RemoveReference(*ref, host.Vm) + + vm.Runtime.Host = ref + } + + return nil, nil + }) + + task.Run() + + return &methods.RelocateVM_TaskBody{ + Res: &types.RelocateVM_TaskResponse{ + Returnval: task.Self, + }, + } +} + +func (vm *VirtualMachine) ShutdownGuest(c *types.ShutdownGuest) soap.HasFault { + r := &methods.ShutdownGuestBody{} + // should be poweron + if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOff { + r.Fault_ = Fault("", &types.InvalidPowerState{ + RequestedState: types.VirtualMachinePowerStatePoweredOn, + ExistingState: vm.Runtime.PowerState, + }) + + return r + } + // change state + vm.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff + vm.Summary.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff + + r.Res = new(types.ShutdownGuestResponse) + + return r +} diff --git a/vendor/github.com/vmware/govmomi/simulator/vpx/doc.go b/vendor/github.com/vmware/govmomi/simulator/vpx/doc.go new file mode 100644 index 00000000..ca98ec73 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/vpx/doc.go @@ -0,0 +1,20 @@ +/* +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 vpx contains SOAP responses from a vCenter server, captured using `govc ... -debug`. +*/ +package vpx diff --git a/vendor/github.com/vmware/govmomi/simulator/vpx/root_folder.go b/vendor/github.com/vmware/govmomi/simulator/vpx/root_folder.go new file mode 100644 index 00000000..a1cce0d8 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/vpx/root_folder.go @@ -0,0 +1,64 @@ +/* +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 vpx + +import ( + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +var RootFolder = mo.Folder{ + ManagedEntity: mo.ManagedEntity{ + ExtensibleManagedObject: mo.ExtensibleManagedObject{ + Self: types.ManagedObjectReference{Type: "Folder", Value: "group-d1"}, + Value: nil, + AvailableField: nil, + }, + Parent: (*types.ManagedObjectReference)(nil), + CustomValue: nil, + OverallStatus: "green", + ConfigStatus: "green", + ConfigIssue: nil, + EffectiveRole: []int32{-1}, + Permission: []types.Permission{ + { + DynamicData: types.DynamicData{}, + Entity: &types.ManagedObjectReference{Type: "Folder", Value: "group-d1"}, + Principal: "VSPHERE.LOCAL\\Administrator", + Group: false, + RoleId: -1, + Propagate: true, + }, + { + DynamicData: types.DynamicData{}, + Entity: &types.ManagedObjectReference{Type: "Folder", Value: "group-d1"}, + Principal: "VSPHERE.LOCAL\\Administrators", + Group: true, + RoleId: -1, + Propagate: true, + }, + }, + Name: "Datacenters", + DisabledMethod: nil, + RecentTask: nil, + DeclaredAlarmState: nil, + AlarmActionsEnabled: (*bool)(nil), + Tag: nil, + }, + ChildType: []string{"Folder", "Datacenter"}, + ChildEntity: nil, +} diff --git a/vendor/github.com/vmware/govmomi/simulator/vpx/service_content.go b/vendor/github.com/vmware/govmomi/simulator/vpx/service_content.go new file mode 100644 index 00000000..1c570f80 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/vpx/service_content.go @@ -0,0 +1,79 @@ +/* +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 vpx + +import "github.com/vmware/govmomi/vim25/types" + +var ServiceContent = types.ServiceContent{ + DynamicData: types.DynamicData{}, + RootFolder: types.ManagedObjectReference{Type: "Folder", Value: "group-d1"}, + PropertyCollector: types.ManagedObjectReference{Type: "PropertyCollector", Value: "propertyCollector"}, + ViewManager: &types.ManagedObjectReference{Type: "ViewManager", Value: "ViewManager"}, + About: types.AboutInfo{ + DynamicData: types.DynamicData{}, + Name: "VMware vCenter Server", + FullName: "VMware vCenter Server 6.0.0 build-3634794", + Vendor: "VMware, Inc.", + Version: "6.0.0", + Build: "3634794", + LocaleVersion: "INTL", + LocaleBuild: "000", + OsType: "linux-x64", + ProductLineId: "vpx", + ApiType: "VirtualCenter", + ApiVersion: "6.0", + InstanceUuid: "8e7597d6-f720-4b5c-b9fc-3412faf07d99", + LicenseProductName: "VMware VirtualCenter Server", + LicenseProductVersion: "6.0", + }, + Setting: &types.ManagedObjectReference{Type: "OptionManager", Value: "VpxSettings"}, + UserDirectory: &types.ManagedObjectReference{Type: "UserDirectory", Value: "UserDirectory"}, + SessionManager: &types.ManagedObjectReference{Type: "SessionManager", Value: "SessionManager"}, + AuthorizationManager: &types.ManagedObjectReference{Type: "AuthorizationManager", Value: "AuthorizationManager"}, + ServiceManager: &types.ManagedObjectReference{Type: "ServiceManager", Value: "ServiceMgr"}, + PerfManager: &types.ManagedObjectReference{Type: "PerformanceManager", Value: "PerfMgr"}, + ScheduledTaskManager: &types.ManagedObjectReference{Type: "ScheduledTaskManager", Value: "ScheduledTaskManager"}, + AlarmManager: &types.ManagedObjectReference{Type: "AlarmManager", Value: "AlarmManager"}, + EventManager: &types.ManagedObjectReference{Type: "EventManager", Value: "EventManager"}, + TaskManager: &types.ManagedObjectReference{Type: "TaskManager", Value: "TaskManager"}, + ExtensionManager: &types.ManagedObjectReference{Type: "ExtensionManager", Value: "ExtensionManager"}, + CustomizationSpecManager: &types.ManagedObjectReference{Type: "CustomizationSpecManager", Value: "CustomizationSpecManager"}, + CustomFieldsManager: &types.ManagedObjectReference{Type: "CustomFieldsManager", Value: "CustomFieldsManager"}, + AccountManager: (*types.ManagedObjectReference)(nil), + DiagnosticManager: &types.ManagedObjectReference{Type: "DiagnosticManager", Value: "DiagMgr"}, + LicenseManager: &types.ManagedObjectReference{Type: "LicenseManager", Value: "LicenseManager"}, + SearchIndex: &types.ManagedObjectReference{Type: "SearchIndex", Value: "SearchIndex"}, + FileManager: &types.ManagedObjectReference{Type: "FileManager", Value: "FileManager"}, + DatastoreNamespaceManager: &types.ManagedObjectReference{Type: "DatastoreNamespaceManager", Value: "DatastoreNamespaceManager"}, + VirtualDiskManager: &types.ManagedObjectReference{Type: "VirtualDiskManager", Value: "virtualDiskManager"}, + VirtualizationManager: (*types.ManagedObjectReference)(nil), + SnmpSystem: &types.ManagedObjectReference{Type: "HostSnmpSystem", Value: "SnmpSystem"}, + VmProvisioningChecker: &types.ManagedObjectReference{Type: "VirtualMachineProvisioningChecker", Value: "ProvChecker"}, + VmCompatibilityChecker: &types.ManagedObjectReference{Type: "VirtualMachineCompatibilityChecker", Value: "CompatChecker"}, + OvfManager: &types.ManagedObjectReference{Type: "OvfManager", Value: "OvfManager"}, + IpPoolManager: &types.ManagedObjectReference{Type: "IpPoolManager", Value: "IpPoolManager"}, + DvSwitchManager: &types.ManagedObjectReference{Type: "DistributedVirtualSwitchManager", Value: "DVSManager"}, + HostProfileManager: &types.ManagedObjectReference{Type: "HostProfileManager", Value: "HostProfileManager"}, + ClusterProfileManager: &types.ManagedObjectReference{Type: "ClusterProfileManager", Value: "ClusterProfileManager"}, + ComplianceManager: &types.ManagedObjectReference{Type: "ProfileComplianceManager", Value: "MoComplianceManager"}, + LocalizationManager: &types.ManagedObjectReference{Type: "LocalizationManager", Value: "LocalizationManager"}, + StorageResourceManager: &types.ManagedObjectReference{Type: "StorageResourceManager", Value: "StorageResourceManager"}, + GuestOperationsManager: &types.ManagedObjectReference{Type: "GuestOperationsManager", Value: "guestOperationsManager"}, + OverheadMemoryManager: &types.ManagedObjectReference{Type: "OverheadMemoryManager", Value: "OverheadMemoryManger"}, + CertificateManager: &types.ManagedObjectReference{Type: "CertificateManager", Value: "certificateManager"}, + IoFilterManager: &types.ManagedObjectReference{Type: "IoFilterManager", Value: "IoFilterManager"}, +} diff --git a/vendor/github.com/vmware/govmomi/simulator/vpx/setting.go b/vendor/github.com/vmware/govmomi/simulator/vpx/setting.go new file mode 100644 index 00000000..dfbb28b7 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/simulator/vpx/setting.go @@ -0,0 +1,60 @@ +/* +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 vpx + +import "github.com/vmware/govmomi/vim25/types" + +// Setting is captured from VC's ServiceContent.OptionManager.setting +var Setting = []types.BaseOptionValue{ + // This list is currently pruned to include sso options only with sso.enabled set to false + &types.OptionValue{ + Key: "config.vpxd.sso.sts.uri", + Value: "https://127.0.0.1/sts/STSService/vsphere.local", + }, + &types.OptionValue{ + Key: "config.vpxd.sso.solutionUser.privateKey", + Value: "/etc/vmware-vpx/ssl/vcsoluser.key", + }, + &types.OptionValue{ + Key: "config.vpxd.sso.solutionUser.name", + Value: "vpxd-b643d01c-928f-469b-96a5-d571d762a78e@vsphere.local", + }, + &types.OptionValue{ + Key: "config.vpxd.sso.solutionUser.certificate", + Value: "/etc/vmware-vpx/ssl/vcsoluser.crt", + }, + &types.OptionValue{ + Key: "config.vpxd.sso.groupcheck.uri", + Value: "https://127.0.0.1/sso-adminserver/sdk/vsphere.local", + }, + &types.OptionValue{ + Key: "config.vpxd.sso.enabled", + Value: "false", + }, + &types.OptionValue{ + Key: "config.vpxd.sso.default.isGroup", + Value: "false", + }, + &types.OptionValue{ + Key: "config.vpxd.sso.default.admin", + Value: "Administrator@vsphere.local", + }, + &types.OptionValue{ + Key: "config.vpxd.sso.admin.uri", + Value: "https://127.0.0.1/sso-adminserver/sdk/vsphere.local", + }, +} diff --git a/vendor/github.com/vmware/govmomi/task/wait.go b/vendor/github.com/vmware/govmomi/task/wait.go index 379093ee..19fee538 100644 --- a/vendor/github.com/vmware/govmomi/task/wait.go +++ b/vendor/github.com/vmware/govmomi/task/wait.go @@ -68,6 +68,11 @@ func (t *taskCallback) fn(pc []types.PropertyChange) bool { 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. diff --git a/vendor/github.com/vmware/govmomi/toolbox/backdoor.go b/vendor/github.com/vmware/govmomi/toolbox/backdoor.go new file mode 100644 index 00000000..62927316 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/backdoor.go @@ -0,0 +1,80 @@ +/* +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 toolbox + +import ( + "errors" + + "github.com/vmware/vmw-guestinfo/message" + "github.com/vmware/vmw-guestinfo/vmcheck" +) + +const ( + rpciProtocol uint32 = 0x49435052 + tcloProtocol uint32 = 0x4f4c4354 +) + +var ( + ErrNotVirtualWorld = errors.New("not in a virtual world") +) + +type backdoorChannel struct { + protocol uint32 + + *message.Channel +} + +func (b *backdoorChannel) Start() error { + if !vmcheck.IsVirtualCPU() { + return ErrNotVirtualWorld + } + + channel, err := message.NewChannel(b.protocol) + if err != nil { + return err + } + + b.Channel = channel + + return nil +} + +func (b *backdoorChannel) Stop() error { + if b.Channel == nil { + return nil + } + + err := b.Channel.Close() + + b.Channel = nil + + return err +} + +// NewBackdoorChannelOut creates a Channel for use with the RPCI protocol +func NewBackdoorChannelOut() Channel { + return &backdoorChannel{ + protocol: rpciProtocol, + } +} + +// NewBackdoorChannelIn creates a Channel for use with the TCLO protocol +func NewBackdoorChannelIn() Channel { + return &backdoorChannel{ + protocol: tcloProtocol, + } +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/channel.go b/vendor/github.com/vmware/govmomi/toolbox/channel.go new file mode 100644 index 00000000..8d217833 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/channel.go @@ -0,0 +1,58 @@ +/* +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 toolbox + +import ( + "bytes" + "fmt" +) + +// Channel abstracts the guest<->vmx RPC transport +type Channel interface { + Start() error + Stop() error + Send([]byte) error + Receive() ([]byte, error) +} + +var ( + rpciOK = []byte{'1', ' '} + rpciERR = []byte{'0', ' '} +) + +// ChannelOut extends Channel to provide RPCI protocol helpers +type ChannelOut struct { + Channel +} + +// Request sends an RPC command to the vmx and checks the return code for success or error +func (c *ChannelOut) Request(request []byte) ([]byte, error) { + if err := c.Send(request); err != nil { + return nil, err + } + + reply, err := c.Receive() + if err != nil { + return nil, err + } + + if bytes.HasPrefix(reply, rpciOK) { + return reply[2:], nil + } + + return nil, fmt.Errorf("request %q: %q", request, reply) +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/command.go b/vendor/github.com/vmware/govmomi/toolbox/command.go new file mode 100644 index 00000000..84d4b808 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/command.go @@ -0,0 +1,708 @@ +/* +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 toolbox + +import ( + "bytes" + "encoding/base64" + "encoding/binary" + "encoding/hex" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/vmware/govmomi/toolbox/hgfs" + "github.com/vmware/govmomi/toolbox/vix" +) + +type CommandHandler func(vix.CommandRequestHeader, []byte) ([]byte, error) + +type CommandServer struct { + Out *ChannelOut + + ProcessManager *ProcessManager + + Authenticate func(vix.CommandRequestHeader, []byte) error + + ProcessStartCommand func(*ProcessManager, *vix.StartProgramRequest) (int64, error) + + handlers map[uint32]CommandHandler + + FileServer *hgfs.Server +} + +func registerCommandServer(service *Service) *CommandServer { + server := &CommandServer{ + Out: service.out, + ProcessManager: NewProcessManager(), + } + + server.handlers = map[uint32]CommandHandler{ + vix.CommandGetToolsState: server.GetToolsState, + vix.CommandStartProgram: server.StartCommand, + vix.CommandTerminateProcess: server.KillProcess, + vix.CommandListProcessesEx: server.ListProcesses, + vix.CommandReadEnvVariables: server.ReadEnvironmentVariables, + vix.CommandCreateTemporaryFileEx: server.CreateTemporaryFile, + vix.CommandCreateTemporaryDirectory: server.CreateTemporaryDirectory, + vix.CommandDeleteGuestFileEx: server.DeleteFile, + vix.CommandCreateDirectoryEx: server.CreateDirectory, + vix.CommandDeleteGuestDirectoryEx: server.DeleteDirectory, + vix.CommandMoveGuestFileEx: server.MoveFile, + vix.CommandMoveGuestDirectory: server.MoveDirectory, + vix.CommandListFiles: server.ListFiles, + vix.CommandSetGuestFileAttributes: server.SetGuestFileAttributes, + vix.CommandInitiateFileTransferFromGuest: server.InitiateFileTransferFromGuest, + vix.CommandInitiateFileTransferToGuest: server.InitiateFileTransferToGuest, + vix.HgfsSendPacketCommand: server.ProcessHgfsPacket, + } + + server.ProcessStartCommand = DefaultStartCommand + + service.RegisterHandler("Vix_1_Relayed_Command", server.Dispatch) + + return server +} + +func commandResult(header vix.CommandRequestHeader, rc int, err error, response []byte) []byte { + // All Foundry tools commands return results that start with a foundry error + // and a guest-OS-specific error (e.g. errno) + errno := 0 + + if err != nil { + // TODO: inspect err for system error, setting errno + + response = []byte(err.Error()) + + log.Printf("[vix] op=%d error: %s", header.OpCode, err) + } + + buf := bytes.NewBufferString(fmt.Sprintf("%d %d ", rc, errno)) + + if header.CommonFlags&vix.CommandGuestReturnsBinary != 0 { + // '#' delimits end of ascii and the start of the binary data (see ToolsDaemonTcloReceiveVixCommand) + _ = buf.WriteByte('#') + } + + _, _ = buf.Write(response) + + if header.CommonFlags&vix.CommandGuestReturnsBinary == 0 { + // this is not binary data, so it should be a NULL terminated string (see ToolsDaemonTcloReceiveVixCommand) + _ = buf.WriteByte(0) + } + + return buf.Bytes() +} + +func (c *CommandServer) Dispatch(data []byte) ([]byte, error) { + // See ToolsDaemonTcloGetQuotedString + if data[0] == '"' { + data = data[1:] + } + + var name string + + ix := bytes.IndexByte(data, '"') + if ix > 0 { + name = string(data[:ix]) + data = data[ix+1:] + } + // skip the NULL + if data[0] == 0 { + data = data[1:] + } + + if Trace { + fmt.Fprintf(os.Stderr, "vix dispatch %q...\n%s\n", name, hex.Dump(data)) + } + + var header vix.CommandRequestHeader + buf := bytes.NewBuffer(data) + err := binary.Read(buf, binary.LittleEndian, &header) + if err != nil { + return nil, err + } + + if header.Magic != vix.CommandMagicWord { + return commandResult(header, vix.InvalidMessageHeader, nil, nil), nil + } + + handler, ok := c.handlers[header.OpCode] + if !ok { + return commandResult(header, vix.UnrecognizedCommandInGuest, nil, nil), nil + } + + if header.OpCode != vix.CommandGetToolsState { + // Every command expect GetToolsState requires authentication + creds := buf.Bytes()[header.BodyLength:] + + err = c.authenticate(header, creds[:header.CredentialLength]) + if err != nil { + return commandResult(header, vix.AuthenticationFail, err, nil), nil + } + } + + rc := vix.OK + + response, err := handler(header, buf.Bytes()) + if err != nil { + rc = vix.ErrorCode(err) + } + + return commandResult(header, rc, err, response), nil +} + +func (c *CommandServer) RegisterHandler(op uint32, handler CommandHandler) { + c.handlers[op] = handler +} + +func (c *CommandServer) GetToolsState(_ vix.CommandRequestHeader, _ []byte) ([]byte, error) { + hostname, _ := os.Hostname() + osname := fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH) + + // Note that vmtoolsd sends back 40 or so of these properties, sticking with the minimal set for now. + props := vix.PropertyList{ + vix.NewStringProperty(vix.PropertyGuestOsVersion, osname), + vix.NewStringProperty(vix.PropertyGuestOsVersionShort, osname), + vix.NewStringProperty(vix.PropertyGuestToolsProductNam, "VMware Tools (Go)"), + vix.NewStringProperty(vix.PropertyGuestToolsVersion, "10.0.5 build-3227872 (Compatible)"), + vix.NewStringProperty(vix.PropertyGuestName, hostname), + vix.NewInt32Property(vix.PropertyGuestToolsAPIOptions, 0x0001), // TODO: const VIX_TOOLSFEATURE_SUPPORT_GET_HANDLE_STATE + vix.NewInt32Property(vix.PropertyGuestOsFamily, 1), // TODO: const GUEST_OS_FAMILY_* + vix.NewBoolProperty(vix.PropertyGuestStartProgramEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestTerminateProcessEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestListProcessesEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestReadEnvironmentVariableEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestMakeDirectoryEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestDeleteFileEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestDeleteDirectoryEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestMoveDirectoryEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestMoveFileEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestCreateTempFileEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestCreateTempDirectoryEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestListFilesEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestChangeFileAttributesEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestInitiateFileTransferFromGuestEnabled, true), + vix.NewBoolProperty(vix.PropertyGuestInitiateFileTransferToGuestEnabled, true), + } + + src, _ := props.MarshalBinary() + enc := base64.StdEncoding + buf := make([]byte, enc.EncodedLen(len(src))) + enc.Encode(buf, src) + + return buf, nil +} + +func (c *CommandServer) StartCommand(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.StartProgramRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + pid, err := c.ProcessStartCommand(c.ProcessManager, r) + if err != nil { + return nil, err + } + + return append([]byte(fmt.Sprintf("%d", pid)), 0), nil +} + +func DefaultStartCommand(m *ProcessManager, r *vix.StartProgramRequest) (int64, error) { + p := NewProcess() + + switch r.ProgramPath { + case "http.RoundTrip": + p = NewProcessRoundTrip() + default: + // Standard vmware-tools requires an absolute path, + // we'll enable IO redirection by default without an absolute path. + if !strings.Contains(r.ProgramPath, "/") { + p = p.WithIO() + } + } + + return m.Start(r, p) +} + +func (c *CommandServer) KillProcess(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.KillProcessRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + if c.ProcessManager.Kill(r.Body.Pid) { + return nil, err + } + + // TODO: could kill process started outside of toolbox + + return nil, vix.Error(vix.NoSuchProcess) +} + +func (c *CommandServer) ListProcesses(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.ListProcessesRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + state := c.ProcessManager.ListProcesses(r.Pids) + + return state, nil +} + +func (c *CommandServer) ReadEnvironmentVariables(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.ReadEnvironmentVariablesRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + buf := new(bytes.Buffer) + + if len(r.Names) == 0 { + for _, e := range os.Environ() { + _, _ = buf.WriteString(fmt.Sprintf("%s", xmlEscape.Replace(e))) + } + } else { + for _, key := range r.Names { + val := os.Getenv(key) + if val == "" { + continue + } + _, _ = buf.WriteString(fmt.Sprintf("%s=%s", xmlEscape.Replace(key), xmlEscape.Replace(val))) + } + } + + return buf.Bytes(), nil +} + +func (c *CommandServer) CreateTemporaryFile(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.CreateTempFileRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + f, err := ioutil.TempFile(r.DirectoryPath, r.FilePrefix+"vmware") + if err != nil { + return nil, err + } + + _ = f.Close() + + return []byte(f.Name()), nil +} + +func (c *CommandServer) CreateTemporaryDirectory(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.CreateTempFileRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + name, err := ioutil.TempDir(r.DirectoryPath, r.FilePrefix+"vmware") + if err != nil { + return nil, err + } + + return []byte(name), nil +} + +func (c *CommandServer) DeleteFile(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.FileRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + info, err := os.Stat(r.GuestPathName) + if err != nil { + return nil, err + } + + if info.IsDir() { + return nil, vix.Error(vix.NotAFile) + } + + err = os.Remove(r.GuestPathName) + + return nil, err +} + +func (c *CommandServer) DeleteDirectory(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.DirRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + info, err := os.Stat(r.GuestPathName) + if err != nil { + return nil, err + } + + if !info.IsDir() { + return nil, vix.Error(vix.NotADirectory) + } + + if r.Body.Recursive { + err = os.RemoveAll(r.GuestPathName) + } else { + err = os.Remove(r.GuestPathName) + } + + return nil, err +} + +func (c *CommandServer) CreateDirectory(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.DirRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + mkdir := os.Mkdir + + if r.Body.Recursive { + mkdir = os.MkdirAll + } + + err = mkdir(r.GuestPathName, 0700) + + return nil, err +} + +func (c *CommandServer) MoveDirectory(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.RenameFileRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + info, err := os.Stat(r.OldPathName) + if err != nil { + return nil, err + } + + if !info.IsDir() { + return nil, vix.Error(vix.NotADirectory) + } + + if !r.Body.Overwrite { + info, err = os.Stat(r.NewPathName) + if err == nil { + return nil, vix.Error(vix.FileAlreadyExists) + } + } + + return nil, os.Rename(r.OldPathName, r.NewPathName) +} + +func (c *CommandServer) MoveFile(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.RenameFileRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + info, err := os.Stat(r.OldPathName) + if err != nil { + return nil, err + } + + if info.IsDir() { + return nil, vix.Error(vix.NotAFile) + } + + if !r.Body.Overwrite { + info, err = os.Stat(r.NewPathName) + if err == nil { + return nil, vix.Error(vix.FileAlreadyExists) + } + } + + return nil, os.Rename(r.OldPathName, r.NewPathName) +} + +func (c *CommandServer) ListFiles(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.ListFilesRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + info, err := os.Lstat(r.GuestPathName) + if err != nil { + return nil, err + } + + var dir string + var files []os.FileInfo + + if info.IsDir() { + dir = r.GuestPathName + files, err = ioutil.ReadDir(r.GuestPathName) + if err != nil { + return nil, err + } + } else { + dir = filepath.Dir(r.GuestPathName) + files = append(files, info) + } + + offset := r.Body.Offset + uint64(r.Body.Index) + total := uint64(len(files)) - offset + + files = files[offset:] + + var remaining uint64 + + if r.Body.MaxResults > 0 && total > uint64(r.Body.MaxResults) { + remaining = total - uint64(r.Body.MaxResults) + files = files[:r.Body.MaxResults] + } + + buf := new(bytes.Buffer) + buf.WriteString(fmt.Sprintf("%d", remaining)) + + for _, info = range files { + buf.WriteString(fileExtendedInfoFormat(dir, info)) + } + + return buf.Bytes(), nil +} + +func chtimes(r *vix.SetGuestFileAttributesRequest) error { + var mtime, atime *time.Time + + if r.IsSet(vix.FileAttributeSetModifyDate) { + t := time.Unix(r.Body.ModificationTime, 0) + mtime = &t + } + + if r.IsSet(vix.FileAttributeSetAccessDate) { + t := time.Unix(r.Body.AccessTime, 0) + atime = &t + } + + if mtime == nil && atime == nil { + return nil + } + + info, err := os.Stat(r.GuestPathName) + if err != nil { + return err + } + + if mtime == nil { + t := info.ModTime() + mtime = &t + } + + if atime == nil { + t := info.ModTime() + atime = &t + } + + return os.Chtimes(r.GuestPathName, *atime, *mtime) +} + +func chown(r *vix.SetGuestFileAttributesRequest) error { + uid := -1 + gid := -1 + + if r.IsSet(vix.FileAttributeSetUnixOwnerid) { + uid = int(r.Body.OwnerID) + } + + if r.IsSet(vix.FileAttributeSetUnixGroupid) { + gid = int(r.Body.GroupID) + } + + if uid == -1 && gid == -1 { + return nil + } + + return os.Chown(r.GuestPathName, uid, gid) +} + +func chmod(r *vix.SetGuestFileAttributesRequest) error { + if r.IsSet(vix.FileAttributeSetUnixPermissions) { + return os.Chmod(r.GuestPathName, os.FileMode(r.Body.Permissions).Perm()) + } + + return nil +} + +func (c *CommandServer) SetGuestFileAttributes(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.SetGuestFileAttributesRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + for _, set := range []func(*vix.SetGuestFileAttributesRequest) error{chtimes, chown, chmod} { + err = set(r) + if err != nil { + return nil, err + } + } + + return nil, nil +} + +func (c *CommandServer) InitiateFileTransferFromGuest(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.ListFilesRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + info, err := c.FileServer.Stat(r.GuestPathName) + if err != nil { + return nil, err + } + + if info.Mode()&os.ModeSymlink == os.ModeSymlink { + return nil, vix.Error(vix.InvalidArg) + } + + if info.IsDir() { + return nil, vix.Error(vix.NotAFile) + } + + return []byte(fileExtendedInfoFormat("", info)), nil +} + +func (c *CommandServer) InitiateFileTransferToGuest(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.InitiateFileTransferToGuestRequest{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + info, err := c.FileServer.Stat(r.GuestPathName) + if err == nil { + if info.Mode()&os.ModeSymlink == os.ModeSymlink { + return nil, vix.Error(vix.InvalidArg) + } + + if info.IsDir() { + return nil, vix.Error(vix.NotAFile) + } + + if !r.Body.Overwrite { + return nil, vix.Error(vix.FileAlreadyExists) + } + } else { + if !os.IsNotExist(err) { + return nil, err + } + } + + return nil, nil +} + +func (c *CommandServer) ProcessHgfsPacket(header vix.CommandRequestHeader, data []byte) ([]byte, error) { + r := &vix.CommandHgfsSendPacket{ + CommandRequestHeader: header, + } + + err := r.UnmarshalBinary(data) + if err != nil { + return nil, err + } + + return c.FileServer.Dispatch(r.Packet) +} + +func (c *CommandServer) authenticate(r vix.CommandRequestHeader, data []byte) error { + if c.Authenticate != nil { + return c.Authenticate(r, data) + } + + switch r.UserCredentialType { + case vix.UserCredentialTypeNamePassword: + var c vix.UserCredentialNamePassword + + if err := c.UnmarshalBinary(data); err != nil { + return err + } + + if Trace { + fmt.Fprintf(traceLog, "ignoring credentials: %q:%q\n", c.Name, c.Password) + } + + return nil + default: + return fmt.Errorf("unsupported UserCredentialType=%d", r.UserCredentialType) + } +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/guest_info.go b/vendor/github.com/vmware/govmomi/toolbox/guest_info.go new file mode 100644 index 00000000..9054cedf --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/guest_info.go @@ -0,0 +1,202 @@ +/* +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 toolbox + +import ( + "bytes" + "fmt" + "net" + + "github.com/davecgh/go-xdr/xdr2" +) + +// Defs from: open-vm-tools/lib/guestRpc/nicinfo.x + +type TypedIPAddress struct { + Type int32 + Address []byte +} + +type IPAddressEntry struct { + Address TypedIPAddress + PrefixLength uint32 + Origin *int32 `xdr:"optional"` + Status *int32 `xdr:"optional"` +} + +type InetCidrRouteEntry struct { + Dest TypedIPAddress + PrefixLength uint32 + NextHop *TypedIPAddress `xdr:"optional"` + IfIndex uint32 + Type int32 + Metric uint32 +} + +type DNSConfigInfo struct { + HostName *string `xdr:"optional"` + DomainName *string `xdr:"optional"` + Servers []TypedIPAddress + Search *string `xdr:"optional"` +} + +type WinsConfigInfo struct { + Primary TypedIPAddress + Secondary TypedIPAddress +} + +type DhcpConfigInfo struct { + Enabled bool + Settings string +} + +type GuestNicV3 struct { + MacAddress string + IPs []IPAddressEntry + DNSConfigInfo *DNSConfigInfo `xdr:"optional"` + WinsConfigInfo *WinsConfigInfo `xdr:"optional"` + DhcpConfigInfov4 *DhcpConfigInfo `xdr:"optional"` + DhcpConfigInfov6 *DhcpConfigInfo `xdr:"optional"` +} + +type NicInfoV3 struct { + Nics []GuestNicV3 + Routes []InetCidrRouteEntry + DNSConfigInfo *DNSConfigInfo `xdr:"optional"` + WinsConfigInfo *WinsConfigInfo `xdr:"optional"` + DhcpConfigInfov4 *DhcpConfigInfo `xdr:"optional"` + DhcpConfigInfov6 *DhcpConfigInfo `xdr:"optional"` +} + +type GuestNicInfo struct { + Version int32 + V3 *NicInfoV3 `xdr:"optional"` +} + +func EncodeXDR(val interface{}) ([]byte, error) { + var buf bytes.Buffer + + _, err := xdr.Marshal(&buf, val) + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func DecodeXDR(buf []byte, val interface{}) error { + r := bytes.NewReader(buf) + _, err := xdr.Unmarshal(r, val) + return err +} + +func NewGuestNicInfo() *GuestNicInfo { + return &GuestNicInfo{ + Version: 3, + V3: &NicInfoV3{}, + } +} + +func (nic *GuestNicV3) AddIP(addr net.Addr) { + ip, ok := addr.(*net.IPNet) + if !ok { + return + } + + kind := int32(1) // IAT_IPV4 + if ip.IP.To4() == nil { + kind = 2 // IAT_IPV6 + } else { + ip.IP = ip.IP.To4() // convert to 4-byte representation + } + + size, _ := ip.Mask.Size() + + // nicinfo.x defines enum IpAddressStatus, but vmtoolsd only uses IAS_PREFERRED + var status int32 = 1 // IAS_PREFERRED + + e := IPAddressEntry{ + Address: TypedIPAddress{ + Type: kind, + Address: []byte(ip.IP), + }, + PrefixLength: uint32(size), + Status: &status, + } + + nic.IPs = append(nic.IPs, e) +} + +func GuestInfoCommand(kind int, req []byte) []byte { + request := fmt.Sprintf("SetGuestInfo %d ", kind) + return append([]byte(request), req...) +} + +var ( + netInterfaces = net.Interfaces + maxNics = 16 // guestRpc/nicinfo.x:NICINFO_MAX_NICS +) + +// +func DefaultGuestNicInfo() *GuestNicInfo { + proto := NewGuestNicInfo() + info := proto.V3 + // #nosec: Errors unhandled + ifs, _ := netInterfaces() + + for _, i := range ifs { + if i.Flags&net.FlagLoopback == net.FlagLoopback { + continue + } + + if len(i.HardwareAddr) == 0 { + continue // Not useful from outside the guest without a MAC + } + + // #nosec: Errors unhandled + addrs, _ := i.Addrs() + + if len(addrs) == 0 { + continue // Not useful from outside the guest without an IP + } + + nic := GuestNicV3{ + MacAddress: i.HardwareAddr.String(), + } + + for _, addr := range addrs { + nic.AddIP(addr) + } + + info.Nics = append(info.Nics, nic) + + if len(info.Nics) >= maxNics { + break + } + } + + return proto +} + +func GuestInfoNicInfoRequest() ([]byte, error) { + r, err := EncodeXDR(DefaultGuestNicInfo()) + if err != nil { + return nil, err + } + + return GuestInfoCommand(9 /*INFO_IPADDRESS_V3*/, r), nil +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/hgfs/archive.go b/vendor/github.com/vmware/govmomi/toolbox/hgfs/archive.go new file mode 100644 index 00000000..24908a00 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/hgfs/archive.go @@ -0,0 +1,342 @@ +/* +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 hgfs + +import ( + "archive/tar" + "bufio" + "bytes" + "compress/gzip" + "io" + "io/ioutil" + "log" + "math" + "net/url" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/vmware/govmomi/toolbox/vix" +) + +// ArchiveScheme is the default scheme used to register the archive FileHandler +var ArchiveScheme = "archive" + +// ArchiveHandler implements a FileHandler for transferring directories. +type ArchiveHandler struct { + Read func(*url.URL, *tar.Reader) error + Write func(*url.URL, *tar.Writer) error +} + +// NewArchiveHandler returns a FileHandler implementation for transferring directories using gzip'd tar files. +func NewArchiveHandler() FileHandler { + return &ArchiveHandler{ + Read: archiveRead, + Write: archiveWrite, + } +} + +// Stat implements FileHandler.Stat +func (*ArchiveHandler) Stat(u *url.URL) (os.FileInfo, error) { + switch u.Query().Get("format") { + case "", "tar", "tgz": + // ok + default: + log.Printf("unknown archive format: %q", u) + return nil, vix.Error(vix.InvalidArg) + } + + return &archive{ + name: u.Path, + size: math.MaxInt64, + }, nil +} + +// Open implements FileHandler.Open +func (h *ArchiveHandler) Open(u *url.URL, mode int32) (File, error) { + switch mode { + case OpenModeReadOnly: + return h.newArchiveFromGuest(u) + case OpenModeWriteOnly: + return h.newArchiveToGuest(u) + default: + return nil, os.ErrNotExist + } +} + +// archive implements the hgfs.File and os.FileInfo interfaces. +type archive struct { + name string + size int64 + done func() error + + io.Reader + io.Writer +} + +// Name implementation of the os.FileInfo interface method. +func (a *archive) Name() string { + return a.name +} + +// Size implementation of the os.FileInfo interface method. +func (a *archive) Size() int64 { + return a.size +} + +// Mode implementation of the os.FileInfo interface method. +func (a *archive) Mode() os.FileMode { + return 0600 +} + +// ModTime implementation of the os.FileInfo interface method. +func (a *archive) ModTime() time.Time { + return time.Now() +} + +// IsDir implementation of the os.FileInfo interface method. +func (a *archive) IsDir() bool { + return false +} + +// Sys implementation of the os.FileInfo interface method. +func (a *archive) Sys() interface{} { + return nil +} + +// The trailer is required since TransferFromGuest requires a Content-Length, +// which toolbox doesn't know ahead of time as the gzip'd tarball never touches the disk. +// HTTP clients need to be aware of this and stop reading when they see the 2nd gzip header. +var gzipHeader = []byte{0x1f, 0x8b, 0x08} // rfc1952 {ID1, ID2, CM} + +var gzipTrailer = true + +// newArchiveFromGuest returns an hgfs.File implementation to read a directory as a gzip'd tar. +func (h *ArchiveHandler) newArchiveFromGuest(u *url.URL) (File, error) { + r, w := io.Pipe() + + a := &archive{ + name: u.Path, + done: r.Close, + Reader: r, + Writer: w, + } + + var z io.Writer = w + var c io.Closer = ioutil.NopCloser(nil) + + switch u.Query().Get("format") { + case "tgz": + gz := gzip.NewWriter(w) + z = gz + c = gz + } + + tw := tar.NewWriter(z) + + go func() { + err := h.Write(u, tw) + + _ = tw.Close() + _ = c.Close() + if gzipTrailer { + _, _ = w.Write(gzipHeader) + } + _ = w.CloseWithError(err) + }() + + return a, nil +} + +// newArchiveToGuest returns an hgfs.File implementation to expand a gzip'd tar into a directory. +func (h *ArchiveHandler) newArchiveToGuest(u *url.URL) (File, error) { + r, w := io.Pipe() + + buf := bufio.NewReader(r) + + a := &archive{ + name: u.Path, + Reader: buf, + Writer: w, + } + + var cerr error + var wg sync.WaitGroup + + a.done = func() error { + _ = w.Close() + // We need to wait for unpack to finish to complete its work + // and to propagate the error if any to Close. + wg.Wait() + return cerr + } + + wg.Add(1) + go func() { + defer wg.Done() + + c := func() error { + // Drain the pipe of tar trailer data (two null blocks) + if cerr == nil { + _, _ = io.Copy(ioutil.Discard, a.Reader) + } + return nil + } + + header, _ := buf.Peek(len(gzipHeader)) + + if bytes.Equal(header, gzipHeader) { + gz, err := gzip.NewReader(a.Reader) + if err != nil { + _ = r.CloseWithError(err) + cerr = err + return + } + + c = gz.Close + a.Reader = gz + } + + tr := tar.NewReader(a.Reader) + + cerr = h.Read(u, tr) + + _ = c() + _ = r.CloseWithError(cerr) + }() + + return a, nil +} + +func (a *archive) Close() error { + return a.done() +} + +// archiveRead writes the contents of the given tar.Reader to the given directory. +func archiveRead(u *url.URL, tr *tar.Reader) error { + for { + header, err := tr.Next() + if err != nil { + if err == io.EOF { + return nil + } + return err + } + + name := filepath.Join(u.Path, header.Name) + mode := os.FileMode(header.Mode) + + switch header.Typeflag { + case tar.TypeDir: + err = os.MkdirAll(name, mode) + case tar.TypeReg: + _ = os.MkdirAll(filepath.Dir(name), 0755) + + var f *os.File + + f, err = os.OpenFile(name, os.O_CREATE|os.O_RDWR|os.O_TRUNC, mode) + if err == nil { + _, cerr := io.Copy(f, tr) + err = f.Close() + if cerr != nil { + err = cerr + } + } + case tar.TypeSymlink: + err = os.Symlink(header.Linkname, name) + } + + // TODO: Uid/Gid may not be meaningful here without some mapping. + // The other option to consider would be making use of the guest auth user ID. + // os.Lchown(name, header.Uid, header.Gid) + + if err != nil { + return err + } + } +} + +// archiveWrite writes the contents of the given source directory to the given tar.Writer. +func archiveWrite(u *url.URL, tw *tar.Writer) error { + info, err := os.Stat(u.Path) + if err != nil { + return err + } + + // Note that the VMX will trim any trailing slash. For example: + // "/foo/bar/?prefix=bar/" will end up here as "/foo/bar/?prefix=bar" + // Escape to avoid this: "/for/bar/?prefix=bar%2F" + prefix := u.Query().Get("prefix") + + dir := u.Path + + f := func(file string, fi os.FileInfo, err error) error { + if err != nil { + return filepath.SkipDir + } + + name := strings.TrimPrefix(file, dir) + name = strings.TrimPrefix(name, "/") + + if name == "" { + return nil // this is u.Path itself (which may or may not have a trailing "/") + } + + if prefix != "" { + name = prefix + name + } + + header, _ := tar.FileInfoHeader(fi, name) + + header.Name = name + + if header.Typeflag == tar.TypeDir { + header.Name += "/" + } + + var f *os.File + + if header.Typeflag == tar.TypeReg && fi.Size() != 0 { + f, err = os.Open(file) + if err != nil { + if os.IsPermission(err) { + return nil + } + return err + } + } + + _ = tw.WriteHeader(header) + + if f != nil { + _, err = io.Copy(tw, f) + _ = f.Close() + } + + return err + } + + if info.IsDir() { + return filepath.Walk(u.Path, f) + } + + dir = filepath.Dir(dir) + + return f(u.Path, info, nil) +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/hgfs/encoding.go b/vendor/github.com/vmware/govmomi/toolbox/hgfs/encoding.go new file mode 100644 index 00000000..24f71f6c --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/hgfs/encoding.go @@ -0,0 +1,73 @@ +/* +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 hgfs + +import ( + "bytes" + "encoding" + "encoding/binary" +) + +// MarshalBinary is a wrapper around binary.Write +func MarshalBinary(fields ...interface{}) ([]byte, error) { + buf := new(bytes.Buffer) + + for _, p := range fields { + switch m := p.(type) { + case encoding.BinaryMarshaler: + data, err := m.MarshalBinary() + if err != nil { + return nil, ProtocolError(err) + } + + _, _ = buf.Write(data) + case []byte: + _, _ = buf.Write(m) + case string: + _, _ = buf.WriteString(m) + default: + err := binary.Write(buf, binary.LittleEndian, p) + if err != nil { + return nil, ProtocolError(err) + } + } + } + + return buf.Bytes(), nil +} + +// UnmarshalBinary is a wrapper around binary.Read +func UnmarshalBinary(data []byte, fields ...interface{}) error { + buf := bytes.NewBuffer(data) + + for _, p := range fields { + switch m := p.(type) { + case encoding.BinaryUnmarshaler: + return m.UnmarshalBinary(buf.Bytes()) + case *[]byte: + *m = buf.Bytes() + return nil + default: + err := binary.Read(buf, binary.LittleEndian, p) + if err != nil { + return ProtocolError(err) + } + } + } + + return nil +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/hgfs/hgfs_darwin.go b/vendor/github.com/vmware/govmomi/toolbox/hgfs/hgfs_darwin.go new file mode 100644 index 00000000..8c3dcdec --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/hgfs/hgfs_darwin.go @@ -0,0 +1,24 @@ +/* +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 hgfs + +import ( + "os" +) + +func (a *AttrV2) sysStat(info os.FileInfo) { +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/hgfs/hgfs_linux.go b/vendor/github.com/vmware/govmomi/toolbox/hgfs/hgfs_linux.go new file mode 100644 index 00000000..16794ae5 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/hgfs/hgfs_linux.go @@ -0,0 +1,58 @@ +/* +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 hgfs + +import ( + "os" + "syscall" +) + +const attrMask = AttrValidAllocationSize | + AttrValidAccessTime | AttrValidWriteTime | AttrValidCreateTime | AttrValidChangeTime | + AttrValidSpecialPerms | AttrValidOwnerPerms | AttrValidGroupPerms | AttrValidOtherPerms | AttrValidEffectivePerms | + AttrValidUserID | AttrValidGroupID | AttrValidFileID | AttrValidVolID + +func (a *AttrV2) sysStat(info os.FileInfo) { + sys, ok := info.Sys().(*syscall.Stat_t) + + if !ok { + return + } + + a.AllocationSize = uint64(sys.Blocks * 512) + + nt := func(t syscall.Timespec) uint64 { + return uint64(t.Nano()) // TODO: this is supposed to be Windows NT system time, not needed atm + } + + a.AccessTime = nt(sys.Atim) + a.WriteTime = nt(sys.Mtim) + a.CreationTime = a.WriteTime // see HgfsGetCreationTime + a.AttrChangeTime = nt(sys.Ctim) + + a.SpecialPerms = uint8((sys.Mode & (syscall.S_ISUID | syscall.S_ISGID | syscall.S_ISVTX)) >> 9) + a.OwnerPerms = uint8((sys.Mode & syscall.S_IRWXU) >> 6) + a.GroupPerms = uint8((sys.Mode & syscall.S_IRWXG) >> 3) + a.OtherPerms = uint8(sys.Mode & syscall.S_IRWXO) + + a.UserID = sys.Uid + a.GroupID = sys.Gid + a.HostFileID = sys.Ino + a.VolumeID = uint32(sys.Dev) + + a.Mask |= attrMask +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/hgfs/hgfs_windows.go b/vendor/github.com/vmware/govmomi/toolbox/hgfs/hgfs_windows.go new file mode 100644 index 00000000..8c3dcdec --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/hgfs/hgfs_windows.go @@ -0,0 +1,24 @@ +/* +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 hgfs + +import ( + "os" +) + +func (a *AttrV2) sysStat(info os.FileInfo) { +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/hgfs/protocol.go b/vendor/github.com/vmware/govmomi/toolbox/hgfs/protocol.go new file mode 100644 index 00000000..a91a0269 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/hgfs/protocol.go @@ -0,0 +1,847 @@ +/* +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 hgfs + +import ( + "bytes" + "encoding/binary" + "fmt" + "log" + "os" + "strings" +) + +// See: https://github.com/vmware/open-vm-tools/blob/master/open-vm-tools/lib/include/hgfsProto.h + +// Opcodes for server operations as defined in hgfsProto.h +const ( + OpOpen = iota /* Open file */ + OpRead /* Read from file */ + OpWrite /* Write to file */ + OpClose /* Close file */ + OpSearchOpen /* Start new search */ + OpSearchRead /* Get next search response */ + OpSearchClose /* End a search */ + OpGetattr /* Get file attributes */ + OpSetattr /* Set file attributes */ + OpCreateDir /* Create new directory */ + OpDeleteFile /* Delete a file */ + OpDeleteDir /* Delete a directory */ + OpRename /* Rename a file or directory */ + OpQueryVolumeInfo /* Query volume information */ + OpOpenV2 /* Open file */ + OpGetattrV2 /* Get file attributes */ + OpSetattrV2 /* Set file attributes */ + OpSearchReadV2 /* Get next search response */ + OpCreateSymlink /* Create a symlink */ + OpServerLockChange /* Change the oplock on a file */ + OpCreateDirV2 /* Create a directory */ + OpDeleteFileV2 /* Delete a file */ + OpDeleteDirV2 /* Delete a directory */ + OpRenameV2 /* Rename a file or directory */ + OpOpenV3 /* Open file */ + OpReadV3 /* Read from file */ + OpWriteV3 /* Write to file */ + OpCloseV3 /* Close file */ + OpSearchOpenV3 /* Start new search */ + OpSearchReadV3 /* Read V3 directory entries */ + OpSearchCloseV3 /* End a search */ + OpGetattrV3 /* Get file attributes */ + OpSetattrV3 /* Set file attributes */ + OpCreateDirV3 /* Create new directory */ + OpDeleteFileV3 /* Delete a file */ + OpDeleteDirV3 /* Delete a directory */ + OpRenameV3 /* Rename a file or directory */ + OpQueryVolumeInfoV3 /* Query volume information */ + OpCreateSymlinkV3 /* Create a symlink */ + OpServerLockChangeV3 /* Change the oplock on a file */ + OpWriteWin32StreamV3 /* Write WIN32_STREAM_ID format data to file */ + OpCreateSessionV4 /* Create a session and return host capabilities. */ + OpDestroySessionV4 /* Destroy/close session. */ + OpReadFastV4 /* Read */ + OpWriteFastV4 /* Write */ + OpSetWatchV4 /* Start monitoring directory changes. */ + OpRemoveWatchV4 /* Stop monitoring directory changes. */ + OpNotifyV4 /* Notification for a directory change event. */ + OpSearchReadV4 /* Read V4 directory entries. */ + OpOpenV4 /* Open file */ + OpEnumerateStreamsV4 /* Enumerate alternative named streams for a file. */ + OpGetattrV4 /* Get file attributes */ + OpSetattrV4 /* Set file attributes */ + OpDeleteV4 /* Delete a file or a directory */ + OpLinkmoveV4 /* Rename/move/create hard link. */ + OpFsctlV4 /* Sending FS control requests. */ + OpAccessCheckV4 /* Access check. */ + OpFsyncV4 /* Flush all cached data to the disk. */ + OpQueryVolumeInfoV4 /* Query volume information. */ + OpOplockAcquireV4 /* Acquire OPLOCK. */ + OpOplockBreakV4 /* Break or downgrade OPLOCK. */ + OpLockByteRangeV4 /* Acquire byte range lock. */ + OpUnlockByteRangeV4 /* Release byte range lock. */ + OpQueryEasV4 /* Query extended attributes. */ + OpSetEasV4 /* Add or modify extended attributes. */ + OpNewHeader = 0xff /* Header op, must be unique, distinguishes packet headers. */ +) + +// Status codes +const ( + StatusSuccess = iota + StatusNoSuchFileOrDir + StatusInvalidHandle + StatusOperationNotPermitted + StatusFileExists + StatusNotDirectory + StatusDirNotEmpty + StatusProtocolError + StatusAccessDenied + StatusInvalidName + StatusGenericError + StatusSharingViolation + StatusNoSpace + StatusOperationNotSupported + StatusNameTooLong + StatusInvalidParameter + StatusNotSameDevice + StatusStaleSession + StatusTooManySessions + StatusTransportError +) + +// Flags for attr mask +const ( + AttrValidType = 1 << iota + AttrValidSize + AttrValidCreateTime + AttrValidAccessTime + AttrValidWriteTime + AttrValidChangeTime + AttrValidSpecialPerms + AttrValidOwnerPerms + AttrValidGroupPerms + AttrValidOtherPerms + AttrValidFlags + AttrValidAllocationSize + AttrValidUserID + AttrValidGroupID + AttrValidFileID + AttrValidVolID + AttrValidNonStaticFileID + AttrValidEffectivePerms + AttrValidExtendAttrSize + AttrValidReparsePoint + AttrValidShortName +) + +// HeaderVersion for HGFS protocol version 4 +const HeaderVersion = 0x1 + +// LargePacketMax is maximum size of an hgfs packet +const LargePacketMax = 0xf800 // HGFS_LARGE_PACKET_MAX + +// Packet flags +const ( + PacketFlagRequest = 1 << iota + PacketFlagReply + PacketFlagInfoExterror + PacketFlagValidFlags = 0x7 +) + +// Status is an error type that encapsulates an error status code and the cause +type Status struct { + Err error + Code uint32 +} + +func (s *Status) Error() string { + if s.Err != nil { + return s.Err.Error() + } + + return fmt.Sprintf("hgfs.Status=%d", s.Code) +} + +// errorStatus maps the given error type to a status code +func errorStatus(err error) uint32 { + if x, ok := err.(*Status); ok { + return x.Code + } + + switch { + case os.IsNotExist(err): + return StatusNoSuchFileOrDir + case os.IsExist(err): + return StatusFileExists + case os.IsPermission(err): + return StatusOperationNotPermitted + } + + return StatusGenericError +} + +// ProtocolError wraps the given error as a Status type +func ProtocolError(err error) error { + return &Status{ + Err: err, + Code: StatusProtocolError, + } +} + +// Request as defined in hgfsProto.h:HgfsRequest +type Request struct { + Handle uint32 + Op int32 +} + +// Reply as defined in hgfsProto.h:HgfsReply +type Reply struct { + Handle uint32 + Status uint32 +} + +// Header as defined in hgfsProto.h:HgfsHeader +type Header struct { + Version uint8 + Reserved1 [3]uint8 + Dummy int32 + PacketSize uint32 + HeaderSize uint32 + RequestID uint32 + Op int32 + Status uint32 + Flags uint32 + Information uint32 + SessionID uint64 + Reserved uint64 +} + +var ( + headerSize = uint32(binary.Size(new(Header))) + + packetSize = func(r *Packet) uint32 { + return headerSize + uint32(len(r.Payload)) + } +) + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (h *Header) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, h) + if err != nil { + return fmt.Errorf("reading hgfs header: %s", err) + } + + if h.Dummy != OpNewHeader { + return fmt.Errorf("expected hgfs header with OpNewHeader (%#x), got: %#x", OpNewHeader, h.Dummy) + } + + return nil +} + +// Packet encapsulates an hgfs Header and Payload +type Packet struct { + Header + + Payload []byte +} + +// Reply composes a new Packet with the given payload or error +func (r *Packet) Reply(payload interface{}, err error) ([]byte, error) { + p := new(Packet) + + status := uint32(StatusSuccess) + + if err != nil { + status = errorStatus(err) + } else { + p.Payload, err = MarshalBinary(payload) + if err != nil { + return nil, err + } + } + + p.Header = Header{ + Version: HeaderVersion, + Dummy: OpNewHeader, + PacketSize: headerSize + uint32(len(p.Payload)), + HeaderSize: headerSize, + RequestID: r.RequestID, + Op: r.Op, + Status: status, + Flags: PacketFlagReply, + Information: 0, + SessionID: r.SessionID, + } + + if Trace { + rc := "OK" + if err != nil { + rc = err.Error() + } + fmt.Fprintf(os.Stderr, "[hgfs] response %#v [%s]\n", p.Header, rc) + } else if err != nil { + log.Printf("[hgfs] op=%d error: %s", r.Op, err) + } + + return p.MarshalBinary() +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *Packet) MarshalBinary() ([]byte, error) { + r.Header.PacketSize = packetSize(r) + + buf, _ := MarshalBinary(r.Header) + + return append(buf, r.Payload...), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *Packet) UnmarshalBinary(data []byte) error { + err := r.Header.UnmarshalBinary(data) + if err != nil { + return err + } + + r.Payload = data[r.HeaderSize:r.PacketSize] + + return nil +} + +// Capability as defined in hgfsProto.h:HgfsCapability +type Capability struct { + Op int32 + Flags uint32 +} + +// RequestCreateSessionV4 as defined in hgfsProto.h:HgfsRequestCreateSessionV4 +type RequestCreateSessionV4 struct { + NumCapabilities uint32 + MaxPacketSize uint32 + Flags uint32 + Reserved uint32 + Capabilities []Capability +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *RequestCreateSessionV4) MarshalBinary() ([]byte, error) { + buf := new(bytes.Buffer) + + r.NumCapabilities = uint32(len(r.Capabilities)) + + fields := []*uint32{ + &r.NumCapabilities, + &r.MaxPacketSize, + &r.Flags, + &r.Reserved, + } + + for _, p := range fields { + err := binary.Write(buf, binary.LittleEndian, p) + if err != nil { + return nil, err + } + } + + for i := uint32(0); i < r.NumCapabilities; i++ { + err := binary.Write(buf, binary.LittleEndian, &r.Capabilities[i]) + if err != nil { + return nil, err + } + } + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *RequestCreateSessionV4) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + fields := []*uint32{ + &r.NumCapabilities, + &r.MaxPacketSize, + &r.Flags, + &r.Reserved, + } + + for _, p := range fields { + err := binary.Read(buf, binary.LittleEndian, p) + if err != nil { + return err + } + } + + for i := uint32(0); i < r.NumCapabilities; i++ { + var cap Capability + err := binary.Read(buf, binary.LittleEndian, &cap) + if err != nil { + return err + } + + r.Capabilities = append(r.Capabilities, cap) + } + + return nil +} + +// ReplyCreateSessionV4 as defined in hgfsProto.h:HgfsReplyCreateSessionV4 +type ReplyCreateSessionV4 struct { + SessionID uint64 + NumCapabilities uint32 + MaxPacketSize uint32 + IdentityOffset uint32 + Flags uint32 + Reserved uint32 + Capabilities []Capability +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *ReplyCreateSessionV4) MarshalBinary() ([]byte, error) { + buf := new(bytes.Buffer) + + fields := []interface{}{ + &r.SessionID, + &r.NumCapabilities, + &r.MaxPacketSize, + &r.IdentityOffset, + &r.Flags, + &r.Reserved, + } + + for _, p := range fields { + err := binary.Write(buf, binary.LittleEndian, p) + if err != nil { + return nil, err + } + } + + for i := uint32(0); i < r.NumCapabilities; i++ { + err := binary.Write(buf, binary.LittleEndian, &r.Capabilities[i]) + if err != nil { + return nil, err + } + } + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *ReplyCreateSessionV4) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + fields := []interface{}{ + &r.SessionID, + &r.NumCapabilities, + &r.MaxPacketSize, + &r.IdentityOffset, + &r.Flags, + &r.Reserved, + } + + for _, p := range fields { + err := binary.Read(buf, binary.LittleEndian, p) + if err != nil { + return err + } + } + + for i := uint32(0); i < r.NumCapabilities; i++ { + var cap Capability + err := binary.Read(buf, binary.LittleEndian, &cap) + if err != nil { + return err + } + + r.Capabilities = append(r.Capabilities, cap) + } + + return nil +} + +// RequestDestroySessionV4 as defined in hgfsProto.h:HgfsRequestDestroySessionV4 +type RequestDestroySessionV4 struct { + Reserved uint64 +} + +// ReplyDestroySessionV4 as defined in hgfsProto.h:HgfsReplyDestroySessionV4 +type ReplyDestroySessionV4 struct { + Reserved uint64 +} + +// FileName as defined in hgfsProto.h:HgfsFileName +type FileName struct { + Length uint32 + Name string +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (f *FileName) MarshalBinary() ([]byte, error) { + name := f.Name + f.Length = uint32(len(f.Name)) + if f.Length == 0 { + // field is defined as 'char name[1];', this byte is required for min sizeof() validation + name = "\x00" + } + return MarshalBinary(&f.Length, name) +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (f *FileName) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + _ = binary.Read(buf, binary.LittleEndian, &f.Length) + + f.Name = string(buf.Next(int(f.Length))) + + return nil +} + +const serverPolicyRootShareName = "root" + +// FromString converts name to a FileName +func (f *FileName) FromString(name string) { + name = strings.TrimPrefix(name, "/") + + cp := strings.Split(name, "/") + + cp = append([]string{serverPolicyRootShareName}, cp...) + + f.Name = strings.Join(cp, "\x00") + f.Length = uint32(len(f.Name)) +} + +// Path converts FileName to a string +func (f *FileName) Path() string { + cp := strings.Split(f.Name, "\x00") + + if len(cp) == 0 || cp[0] != serverPolicyRootShareName { + return "" // TODO: not happening until if/when we handle Windows shares + } + + cp[0] = "" + + return strings.Join(cp, "/") +} + +// FileNameV3 as defined in hgfsProto.h:HgfsFileNameV3 +type FileNameV3 struct { + Length uint32 + Flags uint32 + CaseType int32 + ID uint32 + Name string +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (f *FileNameV3) MarshalBinary() ([]byte, error) { + name := f.Name + f.Length = uint32(len(f.Name)) + if f.Length == 0 { + // field is defined as 'char name[1];', this byte is required for min sizeof() validation + name = "\x00" + } + return MarshalBinary(&f.Length, &f.Flags, &f.CaseType, &f.ID, name) +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (f *FileNameV3) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + fields := []interface{}{ + &f.Length, &f.Flags, &f.CaseType, &f.ID, + } + + for _, p := range fields { + if err := binary.Read(buf, binary.LittleEndian, p); err != nil { + return err + } + } + + f.Name = string(buf.Next(int(f.Length))) + + return nil +} + +// FromString converts name to a FileNameV3 +func (f *FileNameV3) FromString(name string) { + p := new(FileName) + p.FromString(name) + f.Name = p.Name + f.Length = p.Length +} + +// Path converts FileNameV3 to a string +func (f *FileNameV3) Path() string { + return (&FileName{Name: f.Name, Length: f.Length}).Path() +} + +// FileType +const ( + FileTypeRegular = iota + FileTypeDirectory + FileTypeSymlink +) + +// AttrV2 as defined in hgfsProto.h:HgfsAttrV2 +type AttrV2 struct { + Mask uint64 + Type int32 + Size uint64 + CreationTime uint64 + AccessTime uint64 + WriteTime uint64 + AttrChangeTime uint64 + SpecialPerms uint8 + OwnerPerms uint8 + GroupPerms uint8 + OtherPerms uint8 + AttrFlags uint64 + AllocationSize uint64 + UserID uint32 + GroupID uint32 + HostFileID uint64 + VolumeID uint32 + EffectivePerms uint32 + Reserved2 uint64 +} + +// RequestGetattrV2 as defined in hgfsProto.h:HgfsRequestGetattrV2 +type RequestGetattrV2 struct { + Request + AttrHint uint64 + Handle uint32 + FileName FileName +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *RequestGetattrV2) MarshalBinary() ([]byte, error) { + return MarshalBinary(&r.Request, &r.AttrHint, &r.Handle, &r.FileName) +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *RequestGetattrV2) UnmarshalBinary(data []byte) error { + return UnmarshalBinary(data, &r.Request, &r.AttrHint, &r.Handle, &r.FileName) +} + +// ReplyGetattrV2 as defined in hgfsProto.h:HgfsReplyGetattrV2 +type ReplyGetattrV2 struct { + Reply + Attr AttrV2 + SymlinkTarget FileName +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *ReplyGetattrV2) MarshalBinary() ([]byte, error) { + return MarshalBinary(&r.Reply, &r.Attr, &r.SymlinkTarget) +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *ReplyGetattrV2) UnmarshalBinary(data []byte) error { + return UnmarshalBinary(data, &r.Reply, &r.Attr, &r.SymlinkTarget) +} + +// RequestSetattrV2 as defined in hgfsProto.h:HgfsRequestSetattrV2 +type RequestSetattrV2 struct { + Request + Hints uint64 + Attr AttrV2 + Handle uint32 + FileName FileName +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *RequestSetattrV2) MarshalBinary() ([]byte, error) { + return MarshalBinary(&r.Request, &r.Hints, &r.Attr, &r.Handle, &r.FileName) +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *RequestSetattrV2) UnmarshalBinary(data []byte) error { + return UnmarshalBinary(data, &r.Request, &r.Hints, &r.Attr, &r.Handle, &r.FileName) +} + +// ReplySetattrV2 as defined in hgfsProto.h:HgfsReplySetattrV2 +type ReplySetattrV2 struct { + Header Reply +} + +// OpenMode +const ( + OpenModeReadOnly = iota + OpenModeWriteOnly + OpenModeReadWrite + OpenModeAccmodes +) + +// OpenFlags +const ( + Open = iota + OpenEmpty + OpenCreate + OpenCreateSafe + OpenCreateEmpty +) + +// Permissions +const ( + PermRead = 4 + PermWrite = 2 + PermExec = 1 +) + +// RequestOpen as defined in hgfsProto.h:HgfsRequestOpen +type RequestOpen struct { + Request + OpenMode int32 + OpenFlags int32 + Permissions uint8 + FileName FileName +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *RequestOpen) MarshalBinary() ([]byte, error) { + return MarshalBinary(&r.Request, &r.OpenMode, &r.OpenFlags, r.Permissions, &r.FileName) +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *RequestOpen) UnmarshalBinary(data []byte) error { + return UnmarshalBinary(data, &r.Request, &r.OpenMode, &r.OpenFlags, &r.Permissions, &r.FileName) +} + +// ReplyOpen as defined in hgfsProto.h:HgfsReplyOpen +type ReplyOpen struct { + Reply + Handle uint32 +} + +// RequestClose as defined in hgfsProto.h:HgfsRequestClose +type RequestClose struct { + Request + Handle uint32 +} + +// ReplyClose as defined in hgfsProto.h:HgfsReplyClose +type ReplyClose struct { + Reply +} + +// Lock type +const ( + LockNone = iota + LockOpportunistic + LockExclusive + LockShared + LockBatch + LockLease +) + +// RequestOpenV3 as defined in hgfsProto.h:HgfsRequestOpenV3 +type RequestOpenV3 struct { + Mask uint64 + OpenMode int32 + OpenFlags int32 + SpecialPerms uint8 + OwnerPerms uint8 + GroupPerms uint8 + OtherPerms uint8 + AttrFlags uint64 + AllocationSize uint64 + DesiredAccess uint32 + ShareAccess uint32 + DesiredLock int32 + Reserved1 uint64 + Reserved2 uint64 + FileName FileNameV3 +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *RequestOpenV3) MarshalBinary() ([]byte, error) { + return MarshalBinary(&r.Mask, &r.OpenMode, &r.OpenFlags, + &r.SpecialPerms, &r.OwnerPerms, &r.GroupPerms, &r.OtherPerms, + &r.AttrFlags, &r.AllocationSize, &r.DesiredAccess, &r.ShareAccess, + &r.DesiredLock, &r.Reserved1, &r.Reserved2, &r.FileName) +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *RequestOpenV3) UnmarshalBinary(data []byte) error { + return UnmarshalBinary(data, &r.Mask, &r.OpenMode, &r.OpenFlags, + &r.SpecialPerms, &r.OwnerPerms, &r.GroupPerms, &r.OtherPerms, + &r.AttrFlags, &r.AllocationSize, &r.DesiredAccess, &r.ShareAccess, + &r.DesiredLock, &r.Reserved1, &r.Reserved2, &r.FileName) +} + +// ReplyOpenV3 as defined in hgfsProto.h:HgfsReplyOpenV3 +type ReplyOpenV3 struct { + Handle uint32 + AcquiredLock int32 + Flags int32 + Reserved uint32 +} + +// RequestReadV3 as defined in hgfsProto.h:HgfsRequestReadV3 +type RequestReadV3 struct { + Handle uint32 + Offset uint64 + RequiredSize uint32 + Reserved uint64 +} + +// ReplyReadV3 as defined in hgfsProto.h:HgfsReplyReadV3 +type ReplyReadV3 struct { + ActualSize uint32 + Reserved uint64 + Payload []byte +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *ReplyReadV3) MarshalBinary() ([]byte, error) { + return MarshalBinary(&r.ActualSize, &r.Reserved, r.Payload) +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *ReplyReadV3) UnmarshalBinary(data []byte) error { + return UnmarshalBinary(data, &r.ActualSize, &r.Reserved, &r.Payload) +} + +// Write flags +const ( + WriteAppend = 1 +) + +// RequestWriteV3 as defined in hgfsProto.h:HgfsRequestWriteV3 +type RequestWriteV3 struct { + Handle uint32 + WriteFlags uint8 + Offset uint64 + RequiredSize uint32 + Reserved uint64 + Payload []byte +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *RequestWriteV3) MarshalBinary() ([]byte, error) { + return MarshalBinary(&r.Handle, &r.WriteFlags, &r.Offset, &r.RequiredSize, &r.Reserved, r.Payload) +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *RequestWriteV3) UnmarshalBinary(data []byte) error { + return UnmarshalBinary(data, &r.Handle, &r.WriteFlags, &r.Offset, &r.RequiredSize, &r.Reserved, &r.Payload) +} + +// ReplyWriteV3 as defined in hgfsProto.h:HgfsReplyWriteV3 +type ReplyWriteV3 struct { + ActualSize uint32 + Reserved uint64 +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/hgfs/server.go b/vendor/github.com/vmware/govmomi/toolbox/hgfs/server.go new file mode 100644 index 00000000..5a233f26 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/hgfs/server.go @@ -0,0 +1,584 @@ +/* +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 hgfs + +import ( + "errors" + "flag" + "fmt" + "io" + "log" + "math/rand" + "net/url" + "os" + "strconv" + "strings" + "sync" + "sync/atomic" +) + +// See: https://github.com/vmware/open-vm-tools/blob/master/open-vm-tools/lib/hgfsServer/hgfsServer.c + +var ( + // Trace enables hgfs packet tracing + Trace = false +) + +// Server provides an HGFS protocol implementation to support guest tools VmxiHgfsSendPacketCommand +type Server struct { + Capabilities []Capability + + handlers map[int32]func(*Packet) (interface{}, error) + schemes map[string]FileHandler + sessions map[uint64]*session + mu sync.Mutex + handle uint32 + + chmod func(string, os.FileMode) error + chown func(string, int, int) error +} + +// NewServer creates a new Server instance with the default handlers +func NewServer() *Server { + if f := flag.Lookup("toolbox.trace"); f != nil { + Trace, _ = strconv.ParseBool(f.Value.String()) + } + + s := &Server{ + sessions: make(map[uint64]*session), + schemes: make(map[string]FileHandler), + chmod: os.Chmod, + chown: os.Chown, + } + + s.handlers = map[int32]func(*Packet) (interface{}, error){ + OpCreateSessionV4: s.CreateSessionV4, + OpDestroySessionV4: s.DestroySessionV4, + OpGetattrV2: s.GetattrV2, + OpSetattrV2: s.SetattrV2, + OpOpen: s.Open, + OpClose: s.Close, + OpOpenV3: s.OpenV3, + OpReadV3: s.ReadV3, + OpWriteV3: s.WriteV3, + } + + for op := range s.handlers { + s.Capabilities = append(s.Capabilities, Capability{Op: op, Flags: 0x1}) + } + + return s +} + +// RegisterFileHandler enables dispatch to handler for the given scheme. +func (s *Server) RegisterFileHandler(scheme string, handler FileHandler) { + if handler == nil { + delete(s.schemes, scheme) + return + } + s.schemes[scheme] = handler +} + +// Dispatch unpacks the given request packet and dispatches to the appropriate handler +func (s *Server) Dispatch(packet []byte) ([]byte, error) { + req := &Packet{} + + err := req.UnmarshalBinary(packet) + if err != nil { + return nil, err + } + + if Trace { + fmt.Fprintf(os.Stderr, "[hgfs] request %#v\n", req.Header) + } + + var res interface{} + + handler, ok := s.handlers[req.Op] + if ok { + res, err = handler(req) + } else { + err = &Status{ + Code: StatusOperationNotSupported, + Err: fmt.Errorf("unsupported Op(%d)", req.Op), + } + } + + return req.Reply(res, err) +} + +// File interface abstracts standard i/o methods to support transfer +// of regular files and archives of directories. +type File interface { + io.Reader + io.Writer + io.Closer + + Name() string +} + +// FileHandler is the plugin interface for hgfs file transport. +type FileHandler interface { + Stat(*url.URL) (os.FileInfo, error) + Open(*url.URL, int32) (File, error) +} + +// urlParse attempts to convert the given name to a URL with scheme for use as FileHandler dispatch. +func urlParse(name string) *url.URL { + var info os.FileInfo + + u, err := url.Parse(name) + if err == nil && u.Scheme == "" { + info, err = os.Stat(u.Path) + if err == nil && info.IsDir() { + u.Scheme = ArchiveScheme // special case for IsDir() + return u + } + } + + u, err = url.Parse(strings.TrimPrefix(name, "/")) // must appear to be an absolute path or hgfs errors + if err != nil { + u = &url.URL{Path: name} + } + + if u.Scheme == "" { + ix := strings.Index(u.Path, "/") + if ix > 0 { + u.Scheme = u.Path[:ix] + u.Path = u.Path[ix:] + } + } + + return u +} + +// OpenFile selects the File implementation based on file type and mode. +func (s *Server) OpenFile(name string, mode int32) (File, error) { + u := urlParse(name) + + if h, ok := s.schemes[u.Scheme]; ok { + f, serr := h.Open(u, mode) + if serr != os.ErrNotExist { + return f, serr + } + } + + switch mode { + case OpenModeReadOnly: + return os.Open(name) + case OpenModeWriteOnly: + flag := os.O_WRONLY | os.O_CREATE | os.O_TRUNC + return os.OpenFile(name, flag, 0600) + default: + return nil, &Status{ + Err: fmt.Errorf("open mode(%d) not supported for file %q", mode, name), + Code: StatusAccessDenied, + } + } +} + +// Stat wraps os.Stat such that we can report directory types as regular files to support archive streaming. +// In the case of standard vmware-tools, attempts to transfer directories result +// with a VIX_E_NOT_A_FILE (see InitiateFileTransfer{To,From}Guest). +// Note that callers on the VMX side that reach this path are only concerned with: +// - does the file exist? +// - size: +// + used for UI progress with desktop Drag-N-Drop operations, which toolbox does not support. +// + sent to as Content-Length header in response to GET of FileTransferInformation.Url, +// if the first ReadV3 size is > HGFS_LARGE_PACKET_MAX +func (s *Server) Stat(name string) (os.FileInfo, error) { + u := urlParse(name) + + if h, ok := s.schemes[u.Scheme]; ok { + sinfo, serr := h.Stat(u) + if serr != os.ErrNotExist { + return sinfo, serr + } + } + + return os.Stat(name) +} + +type session struct { + files map[uint32]File + mu sync.Mutex +} + +// TODO: we currently depend on the VMX to close files and remove sessions, +// which it does provided it can communicate with the toolbox. Let's look at +// adding session expiration when implementing OpenModeWriteOnly support. +func newSession() *session { + return &session{ + files: make(map[uint32]File), + } +} + +func (s *Server) getSession(p *Packet) (*session, error) { + s.mu.Lock() + session, ok := s.sessions[p.SessionID] + s.mu.Unlock() + + if !ok { + return nil, &Status{ + Code: StatusStaleSession, + Err: errors.New("session not found"), + } + } + + return session, nil +} + +func (s *Server) removeSession(id uint64) bool { + s.mu.Lock() + session, ok := s.sessions[id] + delete(s.sessions, id) + s.mu.Unlock() + + if !ok { + return false + } + + session.mu.Lock() + defer session.mu.Unlock() + + for _, f := range session.files { + log.Printf("[hgfs] session %X removed with open file: %s", id, f.Name()) + _ = f.Close() + } + + return true +} + +// open-vm-tools' session max is 1024, there shouldn't be more than a handful at a given time in our use cases +const maxSessions = 24 + +// CreateSessionV4 handls OpCreateSessionV4 requests +func (s *Server) CreateSessionV4(p *Packet) (interface{}, error) { + const SessionMaxPacketSizeValid = 0x1 + + req := new(RequestCreateSessionV4) + err := UnmarshalBinary(p.Payload, req) + if err != nil { + return nil, err + } + + res := &ReplyCreateSessionV4{ + SessionID: uint64(rand.Int63()), + NumCapabilities: uint32(len(s.Capabilities)), + MaxPacketSize: LargePacketMax, + Flags: SessionMaxPacketSizeValid, + Capabilities: s.Capabilities, + } + + s.mu.Lock() + defer s.mu.Unlock() + if len(s.sessions) > maxSessions { + return nil, &Status{Code: StatusTooManySessions} + } + + s.sessions[res.SessionID] = newSession() + + return res, nil +} + +// DestroySessionV4 handls OpDestroySessionV4 requests +func (s *Server) DestroySessionV4(p *Packet) (interface{}, error) { + if s.removeSession(p.SessionID) { + return &ReplyDestroySessionV4{}, nil + } + + return nil, &Status{Code: StatusStaleSession} +} + +// Stat maps os.FileInfo to AttrV2 +func (a *AttrV2) Stat(info os.FileInfo) { + switch { + case info.IsDir(): + a.Type = FileTypeDirectory + case info.Mode()&os.ModeSymlink == os.ModeSymlink: + a.Type = FileTypeSymlink + default: + a.Type = FileTypeRegular + } + + a.Size = uint64(info.Size()) + + a.Mask = AttrValidType | AttrValidSize + + a.sysStat(info) +} + +// GetattrV2 handles OpGetattrV2 requests +func (s *Server) GetattrV2(p *Packet) (interface{}, error) { + res := &ReplyGetattrV2{} + + req := new(RequestGetattrV2) + err := UnmarshalBinary(p.Payload, req) + if err != nil { + return nil, err + } + + name := req.FileName.Path() + info, err := s.Stat(name) + if err != nil { + return nil, err + } + + res.Attr.Stat(info) + + return res, nil +} + +// SetattrV2 handles OpSetattrV2 requests +func (s *Server) SetattrV2(p *Packet) (interface{}, error) { + res := &ReplySetattrV2{} + + req := new(RequestSetattrV2) + err := UnmarshalBinary(p.Payload, req) + if err != nil { + return nil, err + } + + name := req.FileName.Path() + + _, err = os.Stat(name) + if err != nil && os.IsNotExist(err) { + // assuming this is a virtual file + return res, nil + } + + uid := -1 + if req.Attr.Mask&AttrValidUserID == AttrValidUserID { + uid = int(req.Attr.UserID) + } + + gid := -1 + if req.Attr.Mask&AttrValidGroupID == AttrValidGroupID { + gid = int(req.Attr.GroupID) + } + + err = s.chown(name, uid, gid) + if err != nil { + return nil, err + } + + var perm os.FileMode + + if req.Attr.Mask&AttrValidOwnerPerms == AttrValidOwnerPerms { + perm |= os.FileMode(req.Attr.OwnerPerms) << 6 + } + + if req.Attr.Mask&AttrValidGroupPerms == AttrValidGroupPerms { + perm |= os.FileMode(req.Attr.GroupPerms) << 3 + } + + if req.Attr.Mask&AttrValidOtherPerms == AttrValidOtherPerms { + perm |= os.FileMode(req.Attr.OtherPerms) + } + + if perm != 0 { + err = s.chmod(name, perm) + if err != nil { + return nil, err + } + } + + return res, nil +} + +func (s *Server) newHandle() uint32 { + return atomic.AddUint32(&s.handle, 1) +} + +// Open handles OpOpen requests +func (s *Server) Open(p *Packet) (interface{}, error) { + req := new(RequestOpen) + err := UnmarshalBinary(p.Payload, req) + if err != nil { + return nil, err + } + + session, err := s.getSession(p) + if err != nil { + return nil, err + } + + name := req.FileName.Path() + mode := req.OpenMode + + if mode != OpenModeReadOnly { + return nil, &Status{ + Err: fmt.Errorf("open mode(%d) not supported for file %q", mode, name), + Code: StatusAccessDenied, + } + } + + file, err := s.OpenFile(name, mode) + if err != nil { + return nil, err + } + + res := &ReplyOpen{ + Handle: s.newHandle(), + } + + session.mu.Lock() + session.files[res.Handle] = file + session.mu.Unlock() + + return res, nil +} + +// Close handles OpClose requests +func (s *Server) Close(p *Packet) (interface{}, error) { + req := new(RequestClose) + err := UnmarshalBinary(p.Payload, req) + if err != nil { + return nil, err + } + + session, err := s.getSession(p) + if err != nil { + return nil, err + } + + session.mu.Lock() + file, ok := session.files[req.Handle] + if ok { + delete(session.files, req.Handle) + } + session.mu.Unlock() + + if ok { + err = file.Close() + } else { + return nil, &Status{Code: StatusInvalidHandle} + } + + return &ReplyClose{}, err +} + +// OpenV3 handles OpOpenV3 requests +func (s *Server) OpenV3(p *Packet) (interface{}, error) { + req := new(RequestOpenV3) + err := UnmarshalBinary(p.Payload, req) + if err != nil { + return nil, err + } + + session, err := s.getSession(p) + if err != nil { + return nil, err + } + + name := req.FileName.Path() + + if req.DesiredLock != LockNone { + return nil, &Status{ + Err: fmt.Errorf("open lock type=%d not supported for file %q", req.DesiredLock, name), + Code: StatusOperationNotSupported, + } + } + + file, err := s.OpenFile(name, req.OpenMode) + if err != nil { + return nil, err + } + + res := &ReplyOpenV3{ + Handle: s.newHandle(), + } + + session.mu.Lock() + session.files[res.Handle] = file + session.mu.Unlock() + + return res, nil +} + +// ReadV3 handles OpReadV3 requests +func (s *Server) ReadV3(p *Packet) (interface{}, error) { + req := new(RequestReadV3) + err := UnmarshalBinary(p.Payload, req) + if err != nil { + return nil, err + } + + session, err := s.getSession(p) + if err != nil { + return nil, err + } + + session.mu.Lock() + file, ok := session.files[req.Handle] + session.mu.Unlock() + + if !ok { + return nil, &Status{Code: StatusInvalidHandle} + } + + buf := make([]byte, req.RequiredSize) + + // Use ReadFull as Read() of an archive io.Pipe may return much smaller chunks, + // such as when we've read a tar header. + n, err := io.ReadFull(file, buf) + if err != nil && n == 0 { + if err != io.EOF { + return nil, err + } + } + + res := &ReplyReadV3{ + ActualSize: uint32(n), + Payload: buf[:n], + } + + return res, nil +} + +// WriteV3 handles OpWriteV3 requests +func (s *Server) WriteV3(p *Packet) (interface{}, error) { + req := new(RequestWriteV3) + err := UnmarshalBinary(p.Payload, req) + if err != nil { + return nil, err + } + + session, err := s.getSession(p) + if err != nil { + return nil, err + } + + session.mu.Lock() + file, ok := session.files[req.Handle] + session.mu.Unlock() + + if !ok { + return nil, &Status{Code: StatusInvalidHandle} + } + + n, err := file.Write(req.Payload) + if err != nil { + return nil, err + } + + res := &ReplyWriteV3{ + ActualSize: uint32(n), + } + + return res, nil +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/power.go b/vendor/github.com/vmware/govmomi/toolbox/power.go new file mode 100644 index 00000000..918f9be1 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/power.go @@ -0,0 +1,118 @@ +/* +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 toolbox + +import ( + "fmt" + "log" + "os/exec" +) + +// GuestOsState enum as defined in open-vm-tools/lib/include/vmware/guestrpc/powerops.h +const ( + _ = iota + powerStateHalt + powerStateReboot + powerStatePowerOn + powerStateResume + powerStateSuspend +) + +var ( + shutdown = "/sbin/shutdown" +) + +type PowerCommand struct { + Handler func() error + + out *ChannelOut + state int + name string +} + +type PowerCommandHandler struct { + Halt PowerCommand + Reboot PowerCommand + PowerOn PowerCommand + Resume PowerCommand + Suspend PowerCommand +} + +func registerPowerCommandHandler(service *Service) *PowerCommandHandler { + handler := new(PowerCommandHandler) + + handlers := map[string]struct { + cmd *PowerCommand + state int + }{ + "OS_Halt": {&handler.Halt, powerStateHalt}, + "OS_Reboot": {&handler.Reboot, powerStateReboot}, + "OS_PowerOn": {&handler.PowerOn, powerStatePowerOn}, + "OS_Resume": {&handler.Resume, powerStateResume}, + "OS_Suspend": {&handler.Suspend, powerStateSuspend}, + } + + for name, h := range handlers { + *h.cmd = PowerCommand{ + name: name, + state: h.state, + out: service.out, + } + + service.RegisterHandler(name, h.cmd.Dispatch) + } + + return handler +} + +func (c *PowerCommand) Dispatch([]byte) ([]byte, error) { + rc := rpciOK + + log.Printf("dispatching power op %q", c.name) + + if c.Handler == nil { + if c.state == powerStateHalt || c.state == powerStateReboot { + rc = rpciERR + } + } + + msg := fmt.Sprintf("tools.os.statechange.status %s%d\x00", rc, c.state) + + if _, err := c.out.Request([]byte(msg)); err != nil { + log.Printf("unable to send %q: %q", msg, err) + } + + if c.Handler != nil { + if err := c.Handler(); err != nil { + log.Printf("%s: %s", c.name, err) + } + } + + return nil, nil +} + +func Halt() error { + log.Printf("Halting system...") + // #nosec: Subprocess launching with variable + return exec.Command(shutdown, "-h", "now").Run() +} + +func Reboot() error { + log.Printf("Rebooting system...") + // #nosec: Subprocess launching with variable + return exec.Command(shutdown, "-r", "now").Run() +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/process.go b/vendor/github.com/vmware/govmomi/toolbox/process.go new file mode 100644 index 00000000..695b5ee0 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/process.go @@ -0,0 +1,630 @@ +/* +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 toolbox + +import ( + "bytes" + "context" + "fmt" + "io" + "io/ioutil" + "net" + "net/url" + "os" + "os/exec" + "path" + "path/filepath" + "strconv" + "strings" + "sync" + "sync/atomic" + "syscall" + "time" + + "github.com/vmware/govmomi/toolbox/hgfs" + "github.com/vmware/govmomi/toolbox/vix" +) + +var ( + xmlEscape *strings.Replacer + + shell = "/bin/sh" + + defaultOwner = os.Getenv("USER") +) + +func init() { + // See: VixToolsEscapeXMLString + chars := []string{ + `"`, + "%", + "&", + "'", + "<", + ">", + } + + replace := make([]string, 0, len(chars)*2) + + for _, c := range chars { + replace = append(replace, c) + replace = append(replace, url.QueryEscape(c)) + } + + xmlEscape = strings.NewReplacer(replace...) + + // See procMgrPosix.c:ProcMgrStartProcess: + // Prefer bash -c as is uses exec() to replace itself, + // whereas bourne shell does a fork & exec, so two processes are started. + if sh, err := exec.LookPath("bash"); err != nil { + shell = sh + } + + if defaultOwner == "" { + defaultOwner = "toolbox" + } +} + +// ProcessIO encapsulates IO for Go functions and OS commands such that they can interact via the OperationsManager +// without file system disk IO. +type ProcessIO struct { + In struct { + io.Writer + io.Reader + io.Closer // Closer for the write side of the pipe, can be closed via hgfs ops (FileTranfserToGuest) + } + + Out *bytes.Buffer + Err *bytes.Buffer +} + +// ProcessState is the toolbox representation of the GuestProcessInfo type +type ProcessState struct { + Name string + Args string + Owner string + Pid int64 + ExitCode int32 + StartTime int64 + EndTime int64 + + IO *ProcessIO +} + +// WithIO enables toolbox Process IO without file system disk IO. +func (p *Process) WithIO() *Process { + p.IO = &ProcessIO{ + Out: new(bytes.Buffer), + Err: new(bytes.Buffer), + } + + return p +} + +// ProcessFile implements the os.FileInfo interface to enable toolbox interaction with virtual files. +type ProcessFile struct { + io.Reader + io.Writer + io.Closer + + name string + size int +} + +// Name implementation of the os.FileInfo interface method. +func (a *ProcessFile) Name() string { + return a.name +} + +// Size implementation of the os.FileInfo interface method. +func (a *ProcessFile) Size() int64 { + return int64(a.size) +} + +// Mode implementation of the os.FileInfo interface method. +func (a *ProcessFile) Mode() os.FileMode { + if strings.HasSuffix(a.name, "stdin") { + return 0200 + } + return 0400 +} + +// ModTime implementation of the os.FileInfo interface method. +func (a *ProcessFile) ModTime() time.Time { + return time.Now() +} + +// IsDir implementation of the os.FileInfo interface method. +func (a *ProcessFile) IsDir() bool { + return false +} + +// Sys implementation of the os.FileInfo interface method. +func (a *ProcessFile) Sys() interface{} { + return nil +} + +func (s *ProcessState) toXML() string { + const format = "" + + "%s" + + "%s" + + "%d" + + "%s" + + "%d" + + "%d" + + "%d" + + "" + + name := filepath.Base(s.Name) + + argv := []string{s.Name} + + if len(s.Args) != 0 { + argv = append(argv, xmlEscape.Replace(s.Args)) + } + + args := strings.Join(argv, " ") + + exit := atomic.LoadInt32(&s.ExitCode) + end := atomic.LoadInt64(&s.EndTime) + + return fmt.Sprintf(format, name, args, s.Pid, s.Owner, s.StartTime, exit, end) +} + +// Process managed by the ProcessManager. +type Process struct { + ProcessState + + Start func(*Process, *vix.StartProgramRequest) (int64, error) + Wait func() error + Kill context.CancelFunc + + ctx context.Context +} + +// ProcessError can be returned by the Process.Wait function to propagate ExitCode to ProcessState. +type ProcessError struct { + Err error + ExitCode int32 +} + +func (e *ProcessError) Error() string { + return e.Err.Error() +} + +// ProcessManager manages processes within the guest. +// See: http://pubs.vmware.com/vsphere-60/topic/com.vmware.wssdk.apiref.doc/vim.vm.guest.ProcessManager.html +type ProcessManager struct { + wg sync.WaitGroup + mu sync.Mutex + expire time.Duration + entries map[int64]*Process + pids sync.Pool +} + +// NewProcessManager creates a new ProcessManager instance. +func NewProcessManager() *ProcessManager { + // We use pseudo PIDs that don't conflict with OS PIDs, so they can live in the same table. + // For the pseudo PIDs, we use a sync.Pool rather than a plain old counter to avoid the unlikely, + // but possible wrapping should such a counter exceed MaxInt64. + pid := int64(32768) // TODO: /proc/sys/kernel/pid_max + + return &ProcessManager{ + expire: time.Minute * 5, + entries: make(map[int64]*Process), + pids: sync.Pool{ + New: func() interface{} { + return atomic.AddInt64(&pid, 1) + }, + }, + } +} + +// Start calls the Process.Start function, returning the pid on success or an error. +// A goroutine is started that calls the Process.Wait function. After Process.Wait has +// returned, the ProcessState EndTime and ExitCode fields are set. The process state can be +// queried via ListProcessesInGuest until it is removed, 5 minutes after Wait returns. +func (m *ProcessManager) Start(r *vix.StartProgramRequest, p *Process) (int64, error) { + p.Name = r.ProgramPath + p.Args = r.Arguments + + // Owner is cosmetic, but useful for example with: govc guest.ps -U $uid + if p.Owner == "" { + p.Owner = defaultOwner + } + + p.StartTime = time.Now().Unix() + + p.ctx, p.Kill = context.WithCancel(context.Background()) + + pid, err := p.Start(p, r) + if err != nil { + return -1, err + } + + if pid == 0 { + p.Pid = m.pids.Get().(int64) // pseudo pid for funcs + } else { + p.Pid = pid + } + + m.mu.Lock() + m.entries[p.Pid] = p + m.mu.Unlock() + + m.wg.Add(1) + go func() { + werr := p.Wait() + + atomic.StoreInt64(&p.EndTime, time.Now().Unix()) + + if werr != nil { + rc := int32(1) + if xerr, ok := werr.(*ProcessError); ok { + rc = xerr.ExitCode + } + + atomic.StoreInt32(&p.ExitCode, rc) + } + + m.wg.Done() + p.Kill() // cancel context for those waiting on p.ctx.Done() + + // See: http://pubs.vmware.com/vsphere-65/topic/com.vmware.wssdk.apiref.doc/vim.vm.guest.ProcessManager.ProcessInfo.html + // "If the process was started using StartProgramInGuest then the process completion time + // will be available if queried within 5 minutes after it completes." + <-time.After(m.expire) + + m.mu.Lock() + delete(m.entries, p.Pid) + m.mu.Unlock() + + if pid == 0 { + m.pids.Put(p.Pid) // pseudo pid can be reused now + } + }() + + return p.Pid, nil +} + +// Kill cancels the Process Context. +// Returns true if pid exists in the process table, false otherwise. +func (m *ProcessManager) Kill(pid int64) bool { + m.mu.Lock() + entry, ok := m.entries[pid] + m.mu.Unlock() + + if ok { + entry.Kill() + return true + } + + return false +} + +// ListProcesses marshals the ProcessState for the given pids. +// If no pids are specified, all current processes are included. +// The return value can be used for responding to a VixMsgListProcessesExRequest. +func (m *ProcessManager) ListProcesses(pids []int64) []byte { + w := new(bytes.Buffer) + + m.mu.Lock() + + if len(pids) == 0 { + for _, p := range m.entries { + _, _ = w.WriteString(p.toXML()) + } + } else { + for _, id := range pids { + p, ok := m.entries[id] + if !ok { + continue + } + + _, _ = w.WriteString(p.toXML()) + } + } + + m.mu.Unlock() + + return w.Bytes() +} + +type procFileInfo struct { + os.FileInfo +} + +// Size returns hgfs.LargePacketMax such that InitiateFileTransferFromGuest can download a /proc/ file from the guest. +// If we were to return the size '0' here, then a 'Content-Length: 0' header is returned by VC/ESX. +func (p procFileInfo) Size() int64 { + return hgfs.LargePacketMax // Remember, Sully, when I promised to kill you last? I lied. +} + +// Stat implements hgfs.FileHandler.Stat +func (m *ProcessManager) Stat(u *url.URL) (os.FileInfo, error) { + name := path.Join("/proc", u.Path) + + info, err := os.Stat(name) + if err == nil && info.Size() == 0 { + // This is a real /proc file + return &procFileInfo{info}, nil + } + + dir, file := path.Split(u.Path) + + pid, err := strconv.ParseInt(path.Base(dir), 10, 64) + if err != nil { + return nil, os.ErrNotExist + } + + m.mu.Lock() + p := m.entries[pid] + m.mu.Unlock() + + if p == nil || p.IO == nil { + return nil, os.ErrNotExist + } + + pf := &ProcessFile{ + name: name, + Closer: ioutil.NopCloser(nil), // via hgfs, nop for stdout and stderr + } + + var r *bytes.Buffer + + switch file { + case "stdin": + pf.Writer = p.IO.In.Writer + pf.Closer = p.IO.In.Closer + return pf, nil + case "stdout": + r = p.IO.Out + case "stderr": + r = p.IO.Err + default: + return nil, os.ErrNotExist + } + + select { + case <-p.ctx.Done(): + case <-time.After(time.Second): + // The vmx guest RPC calls are queue based, serialized on the vmx side. + // There are 5 seconds between "ping" RPC calls and after a few misses, + // the vmx considers tools as not running. In this case, the vmx would timeout + // a file transfer after 60 seconds. + // + // vix.FileAccessError is converted to a CannotAccessFile fault, + // so the client can choose to retry the transfer in this case. + // Would have preferred vix.ObjectIsBusy (EBUSY), but VC/ESX converts that + // to a general SystemErrorFault with nothing but a localized string message + // to check against: "vix error codes = (5, 0)." + // Is standard vmware-tools, EACCES is converted to a CannotAccessFile fault. + return nil, vix.Error(vix.FileAccessError) + } + + pf.Reader = r + pf.size = r.Len() + + return pf, nil +} + +// Open implements hgfs.FileHandler.Open +func (m *ProcessManager) Open(u *url.URL, mode int32) (hgfs.File, error) { + info, err := m.Stat(u) + if err != nil { + return nil, err + } + + pinfo, ok := info.(*ProcessFile) + + if !ok { + return nil, os.ErrNotExist // fall through to default os.Open + } + + switch path.Base(u.Path) { + case "stdin": + if mode != hgfs.OpenModeWriteOnly { + return nil, vix.Error(vix.InvalidArg) + } + case "stdout", "stderr": + if mode != hgfs.OpenModeReadOnly { + return nil, vix.Error(vix.InvalidArg) + } + } + + return pinfo, nil +} + +type processFunc struct { + wg sync.WaitGroup + + run func(context.Context, string) error + + err error +} + +// NewProcessFunc creates a new Process, where the Start function calls the given run function within a goroutine. +// The Wait function waits for the goroutine to finish and returns the error returned by run. +// The run ctx param may be used to return early via the ProcessManager.Kill method. +// The run args command is that of the VixMsgStartProgramRequest.Arguments field. +func NewProcessFunc(run func(ctx context.Context, args string) error) *Process { + f := &processFunc{run: run} + + return &Process{ + Start: f.start, + Wait: f.wait, + } +} + +// ProcessFuncIO is the Context key to access optional ProcessIO +var ProcessFuncIO = struct { + key int +}{vix.CommandMagicWord} + +func (f *processFunc) start(p *Process, r *vix.StartProgramRequest) (int64, error) { + f.wg.Add(1) + + var c io.Closer + + if p.IO != nil { + pr, pw := io.Pipe() + + p.IO.In.Reader, p.IO.In.Writer = pr, pw + c, p.IO.In.Closer = pr, pw + + p.ctx = context.WithValue(p.ctx, ProcessFuncIO, p.IO) + } + + go func() { + f.err = f.run(p.ctx, r.Arguments) + + if p.IO != nil { + _ = c.Close() + + if f.err != nil && p.IO.Err.Len() == 0 { + p.IO.Err.WriteString(f.err.Error()) + } + } + + f.wg.Done() + }() + + return 0, nil +} + +func (f *processFunc) wait() error { + f.wg.Wait() + return f.err +} + +type processCmd struct { + cmd *exec.Cmd +} + +// NewProcess creates a new Process, where the Start function use exec.CommandContext to create and start the process. +// The Wait function waits for the process to finish and returns the error returned by exec.Cmd.Wait(). +// Prior to Wait returning, the exec.Cmd.Wait() error is used to set the ProcessState.ExitCode, if error is of type exec.ExitError. +// The ctx param may be used to kill the process via the ProcessManager.Kill method. +// The VixMsgStartProgramRequest param fields are mapped to the exec.Cmd counterpart fields. +// Processes are started within a sub-shell, allowing for i/o redirection, just as with the C version of vmware-tools. +func NewProcess() *Process { + c := new(processCmd) + + return &Process{ + Start: c.start, + Wait: c.wait, + } +} + +func (c *processCmd) start(p *Process, r *vix.StartProgramRequest) (int64, error) { + name, err := exec.LookPath(r.ProgramPath) + if err != nil { + return -1, err + } + // #nosec: Subprocess launching with variable + // Note that processCmd is currently used only for testing. + c.cmd = exec.CommandContext(p.ctx, shell, "-c", fmt.Sprintf("%s %s", name, r.Arguments)) + c.cmd.Dir = r.WorkingDir + c.cmd.Env = r.EnvVars + + if p.IO != nil { + in, perr := c.cmd.StdinPipe() + if perr != nil { + return -1, perr + } + + p.IO.In.Writer = in + p.IO.In.Closer = in + + // Note we currently use a Buffer in addition to the os.Pipe so that: + // - Stat() can provide a size + // - FileTransferFromGuest won't block + // - Can't use the exec.Cmd.Std{out,err}Pipe methods since Wait() closes the pipes. + // We could use os.Pipe directly, but toolbox needs to take care of closing both ends, + // but also need to prevent FileTransferFromGuest from blocking. + c.cmd.Stdout = p.IO.Out + c.cmd.Stderr = p.IO.Err + } + + err = c.cmd.Start() + if err != nil { + return -1, err + } + + return int64(c.cmd.Process.Pid), nil +} + +func (c *processCmd) wait() error { + err := c.cmd.Wait() + if err != nil { + xerr := &ProcessError{ + Err: err, + ExitCode: 1, + } + + if x, ok := err.(*exec.ExitError); ok { + if status, ok := x.Sys().(syscall.WaitStatus); ok { + xerr.ExitCode = int32(status.ExitStatus()) + } + } + + return xerr + } + + return nil +} + +// NewProcessRoundTrip starts a Go function to implement a toolbox backed http.RoundTripper +func NewProcessRoundTrip() *Process { + return NewProcessFunc(func(ctx context.Context, host string) error { + p, _ := ctx.Value(ProcessFuncIO).(*ProcessIO) + + closers := []io.Closer{p.In.Closer} + + defer func() { + for _, c := range closers { + _ = c.Close() + } + }() + + c, err := new(net.Dialer).DialContext(ctx, "tcp", host) + if err != nil { + return err + } + + closers = append(closers, c) + + go func() { + <-ctx.Done() + if ctx.Err() == context.DeadlineExceeded { + _ = c.Close() + } + }() + + _, err = io.Copy(c, p.In.Reader) + if err != nil { + return err + } + + _, err = io.Copy(p.Out, c) + if err != nil { + return err + } + + return nil + }).WithIO() +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/service.go b/vendor/github.com/vmware/govmomi/toolbox/service.go new file mode 100644 index 00000000..81c9f397 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/service.go @@ -0,0 +1,336 @@ +/* +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 toolbox + +import ( + "bytes" + "fmt" + "log" + "net" + "os" + "sync" + "time" + + "github.com/vmware/govmomi/toolbox/hgfs" +) + +const ( + // TOOLS_VERSION_UNMANAGED as defined in open-vm-tools/lib/include/vm_tools_version.h + toolsVersionUnmanaged = 0x7fffffff + + // RPCIN_MAX_DELAY as defined in rpcChannelInt.h: + maxDelay = 10 +) + +var ( + capabilities = []string{ + // Without tools.set.version, the UI reports Tools are "running", but "not installed" + fmt.Sprintf("tools.set.version %d", toolsVersionUnmanaged), + + // Required to invoke guest power operations (shutdown, reboot) + "tools.capability.statechange", + + "tools.capability.hgfs_server toolbox 1", + } + + netInterfaceAddrs = net.InterfaceAddrs + + // If we have an RPCI send error, the channels will be reset. + // open-vm-tools/lib/rpcChannel/rpcChannel.c:RpcChannelCheckReset also backs off in this case + resetDelay = time.Duration(500) // 500 * 10ms == 5s +) + +// Service receives and dispatches incoming RPC requests from the vmx +type Service struct { + name string + in Channel + out *ChannelOut + handlers map[string]Handler + stop chan struct{} + wg *sync.WaitGroup + delay time.Duration + rpcError bool + + Command *CommandServer + Power *PowerCommandHandler + + PrimaryIP func() string +} + +// NewService initializes a Service instance +func NewService(rpcIn Channel, rpcOut Channel) *Service { + s := &Service{ + name: "toolbox", // Same name used by vmtoolsd + in: NewTraceChannel(rpcIn), + out: &ChannelOut{NewTraceChannel(rpcOut)}, + handlers: make(map[string]Handler), + wg: new(sync.WaitGroup), + stop: make(chan struct{}), + + PrimaryIP: DefaultIP, + } + + s.RegisterHandler("reset", s.Reset) + s.RegisterHandler("ping", s.Ping) + s.RegisterHandler("Set_Option", s.SetOption) + s.RegisterHandler("Capabilities_Register", s.CapabilitiesRegister) + + s.Command = registerCommandServer(s) + s.Command.FileServer = hgfs.NewServer() + s.Command.FileServer.RegisterFileHandler("proc", s.Command.ProcessManager) + s.Command.FileServer.RegisterFileHandler(hgfs.ArchiveScheme, hgfs.NewArchiveHandler()) + + s.Power = registerPowerCommandHandler(s) + + return s +} + +// backoff exponentially increases the RPC poll delay up to maxDelay +func (s *Service) backoff() { + if s.delay < maxDelay { + if s.delay > 0 { + d := s.delay * 2 + if d > s.delay && d < maxDelay { + s.delay = d + } else { + s.delay = maxDelay + } + } else { + s.delay = 1 + } + } +} + +func (s *Service) stopChannel() { + _ = s.in.Stop() + _ = s.out.Stop() +} + +func (s *Service) startChannel() error { + err := s.in.Start() + if err != nil { + return err + } + + return s.out.Start() +} + +func (s *Service) checkReset() error { + if s.rpcError { + s.stopChannel() + err := s.startChannel() + if err != nil { + s.delay = resetDelay + return err + } + s.rpcError = false + } + + return nil +} + +// Start initializes the RPC channels and starts a goroutine to listen for incoming RPC requests +func (s *Service) Start() error { + err := s.startChannel() + if err != nil { + return err + } + + s.wg.Add(1) + go func() { + defer s.wg.Done() + + // Same polling interval and backoff logic as vmtoolsd. + // Required in our case at startup at least, otherwise it is possible + // we miss the 1 Capabilities_Register call for example. + + // Note we Send(response) even when nil, to let the VMX know we are here + var response []byte + + for { + select { + case <-s.stop: + s.stopChannel() + return + case <-time.After(time.Millisecond * 10 * s.delay): + if err = s.checkReset(); err != nil { + continue + } + + err = s.in.Send(response) + response = nil + if err != nil { + s.delay = resetDelay + s.rpcError = true + continue + } + + request, _ := s.in.Receive() + + if len(request) > 0 { + response = s.Dispatch(request) + + s.delay = 0 + } else { + s.backoff() + } + } + } + }() + + return nil +} + +// Stop cancels the RPC listener routine created via Start +func (s *Service) Stop() { + close(s.stop) +} + +// Wait blocks until Start returns, allowing any current RPC in progress to complete. +func (s *Service) Wait() { + s.wg.Wait() +} + +// Handler is given the raw argument portion of an RPC request and returns a response +type Handler func([]byte) ([]byte, error) + +// RegisterHandler for the given RPC name +func (s *Service) RegisterHandler(name string, handler Handler) { + s.handlers[name] = handler +} + +// Dispatch an incoming RPC request to a Handler +func (s *Service) Dispatch(request []byte) []byte { + msg := bytes.SplitN(request, []byte{' '}, 2) + name := msg[0] + + // Trim NULL byte terminator + name = bytes.TrimRight(name, "\x00") + + handler, ok := s.handlers[string(name)] + + if !ok { + log.Printf("unknown command: %q\n", name) + return []byte("Unknown Command") + } + + var args []byte + if len(msg) == 2 { + args = msg[1] + } + + response, err := handler(args) + if err == nil { + response = append([]byte("OK "), response...) + } else { + log.Printf("error calling %s: %s\n", name, err) + response = append([]byte("ERR "), response...) + } + + return response +} + +// Reset is the default Handler for reset requests +func (s *Service) Reset([]byte) ([]byte, error) { + s.SendGuestInfo() // Send the IP info ASAP + + return []byte("ATR " + s.name), nil +} + +// Ping is the default Handler for ping requests +func (s *Service) Ping([]byte) ([]byte, error) { + return nil, nil +} + +// SetOption is the default Handler for Set_Option requests +func (s *Service) SetOption(args []byte) ([]byte, error) { + opts := bytes.SplitN(args, []byte{' '}, 2) + key := string(opts[0]) + val := string(opts[1]) + + if Trace { + fmt.Fprintf(os.Stderr, "set option %q=%q\n", key, val) + } + + switch key { + case "broadcastIP": // TODO: const-ify + if val == "1" { + ip := s.PrimaryIP() + if ip == "" { + log.Printf("failed to find primary IP") + return nil, nil + } + msg := fmt.Sprintf("info-set guestinfo.ip %s", ip) + _, err := s.out.Request([]byte(msg)) + if err != nil { + return nil, err + } + + s.SendGuestInfo() + } + default: + // TODO: handle other options... + } + + return nil, nil +} + +// DefaultIP is used by default when responding to a Set_Option broadcastIP request +// It can be overridden with the Service.PrimaryIP field +func DefaultIP() string { + addrs, err := netInterfaceAddrs() + if err == nil { + for _, addr := range addrs { + if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() { + if ip.IP.To4() != nil { + return ip.IP.String() + } + } + } + } + + return "" +} + +func (s *Service) CapabilitiesRegister([]byte) ([]byte, error) { + for _, cap := range capabilities { + _, err := s.out.Request([]byte(cap)) + if err != nil { + log.Printf("send %q: %s", cap, err) + } + } + + return nil, nil +} + +func (s *Service) SendGuestInfo() { + info := []func() ([]byte, error){ + GuestInfoNicInfoRequest, + } + + for i, r := range info { + b, err := r() + + if err == nil { + _, err = s.out.Request(b) + } + + if err != nil { + log.Printf("SendGuestInfo %d: %s", i, err) + } + } +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/toolbox/main.go b/vendor/github.com/vmware/govmomi/toolbox/toolbox/main.go new file mode 100644 index 00000000..083d9f1f --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/toolbox/main.go @@ -0,0 +1,71 @@ +/* +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. +*/ +// Copyright 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 main + +import ( + "flag" + "log" + "os" + "os/signal" + "syscall" + + "github.com/vmware/govmomi/toolbox" +) + +// This example can be run on a VM hosted by ESX, Fusion or Workstation +func main() { + flag.Parse() + + in := toolbox.NewBackdoorChannelIn() + out := toolbox.NewBackdoorChannelOut() + + service := toolbox.NewService(in, out) + + if os.Getuid() == 0 { + service.Power.Halt.Handler = toolbox.Halt + service.Power.Reboot.Handler = toolbox.Reboot + } + + err := service.Start() + if err != nil { + log.Fatal(err) + } + + // handle the signals and gracefully shutdown the service + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) + + go func() { + log.Printf("signal %s received", <-sig) + service.Stop() + }() + + service.Wait() +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/toolbox_darwin.go b/vendor/github.com/vmware/govmomi/toolbox/toolbox_darwin.go new file mode 100644 index 00000000..fb3c1303 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/toolbox_darwin.go @@ -0,0 +1,25 @@ +/* +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 toolbox + +import ( + "os" +) + +func fileExtendedInfoFormat(dir string, info os.FileInfo) string { + return "" +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/toolbox_linux.go b/vendor/github.com/vmware/govmomi/toolbox/toolbox_linux.go new file mode 100644 index 00000000..35e671a9 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/toolbox_linux.go @@ -0,0 +1,69 @@ +/* +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 toolbox + +import ( + "fmt" + "os" + "path/filepath" + "syscall" + "time" + + "github.com/vmware/govmomi/toolbox/vix" +) + +func fileExtendedInfoFormat(dir string, info os.FileInfo) string { + const format = "" + + "%s" + + "%d" + + "%d" + + "%d" + + "%d" + + "%d" + + "%d" + + "%d" + + "%s" + + "" + + props := 0 + targ := "" + + if info.IsDir() { + props |= vix.FileAttributesDirectory + } + + if info.Mode()&os.ModeSymlink == os.ModeSymlink { + props |= vix.FileAttributesSymlink + targ, _ = os.Readlink(filepath.Join(dir, info.Name())) + } + + size := info.Size() + mtime := info.ModTime().Unix() + perm := info.Mode().Perm() + + atime := mtime + uid := os.Getuid() + gid := os.Getgid() + + if sys, ok := info.Sys().(*syscall.Stat_t); ok { + atime = time.Unix(sys.Atim.Unix()).Unix() + uid = int(sys.Uid) + gid = int(sys.Gid) + } + + return fmt.Sprintf(format, info.Name(), props, size, mtime, atime, uid, gid, perm, targ) +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/toolbox_windows.go b/vendor/github.com/vmware/govmomi/toolbox/toolbox_windows.go new file mode 100644 index 00000000..7e5c11e5 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/toolbox_windows.go @@ -0,0 +1,41 @@ +/* +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 toolbox + +import ( + "fmt" + "os" +) + +func fileExtendedInfoFormat(dir string, info os.FileInfo) string { + const format = "" + + "%s" + + "%d" + + "%d" + + "%d" + + "%d" + + "%d" + + "" + + props := 0 + size := info.Size() + mtime := info.ModTime().Unix() + ctime := 0 + atime := 0 + + return fmt.Sprintf(format, info.Name(), props, size, mtime, ctime, atime) +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/trace_channel.go b/vendor/github.com/vmware/govmomi/toolbox/trace_channel.go new file mode 100644 index 00000000..925ce4ce --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/trace_channel.go @@ -0,0 +1,83 @@ +/* +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 toolbox + +import ( + "encoding/hex" + "flag" + "fmt" + "io" + "os" +) + +var ( + Trace = false + + traceLog io.Writer = os.Stderr +) + +func init() { + flag.BoolVar(&Trace, "toolbox.trace", Trace, "Enable toolbox trace") +} + +type TraceChannel struct { + Channel + log io.Writer +} + +func NewTraceChannel(c Channel) Channel { + if !Trace { + return c + } + + return &TraceChannel{ + Channel: c, + log: traceLog, + } +} + +func (d *TraceChannel) Start() error { + err := d.Channel.Start() + + return err +} + +func (d *TraceChannel) Stop() error { + err := d.Channel.Stop() + + return err +} + +func (d *TraceChannel) Send(buf []byte) error { + if len(buf) > 0 { + fmt.Fprintf(d.log, "SEND %d...\n%s\n", len(buf), hex.Dump(buf)) + } + + err := d.Channel.Send(buf) + + return err +} + +func (d *TraceChannel) Receive() ([]byte, error) { + buf, err := d.Channel.Receive() + + if err == nil && len(buf) > 0 { + fmt.Fprintf(d.log, "RECV %d...\n%s\n", len(buf), hex.Dump(buf)) + } + + return buf, err +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/vix/property.go b/vendor/github.com/vmware/govmomi/toolbox/vix/property.go new file mode 100644 index 00000000..aa708c4e --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/vix/property.go @@ -0,0 +1,236 @@ +/* +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 vix + +import ( + "bytes" + "encoding/binary" + "errors" +) + +// Property type enum as defined in open-vm-tools/lib/include/vix.h +const ( + _ = iota // ANY type not supported + vixPropertyTypeInt32 + vixPropertyTypeString + vixPropertyTypeBool + _ // HANDLE type not supported + vixPropertyTypeInt64 + vixPropertyTypeBlob +) + +// Property ID enum as defined in open-vm-tools/lib/include/vixOpenSource.h +const ( + PropertyGuestToolsAPIOptions = 4501 + PropertyGuestOsFamily = 4502 + PropertyGuestOsVersion = 4503 + PropertyGuestToolsProductNam = 4511 + PropertyGuestToolsVersion = 4500 + PropertyGuestName = 4505 + PropertyGuestOsVersionShort = 4520 + + PropertyGuestStartProgramEnabled = 4540 + PropertyGuestListProcessesEnabled = 4541 + PropertyGuestTerminateProcessEnabled = 4542 + PropertyGuestReadEnvironmentVariableEnabled = 4543 + + PropertyGuestMakeDirectoryEnabled = 4547 + PropertyGuestDeleteFileEnabled = 4548 + PropertyGuestDeleteDirectoryEnabled = 4549 + PropertyGuestMoveDirectoryEnabled = 4550 + PropertyGuestMoveFileEnabled = 4551 + PropertyGuestCreateTempFileEnabled = 4552 + PropertyGuestCreateTempDirectoryEnabled = 4553 + PropertyGuestListFilesEnabled = 4554 + PropertyGuestChangeFileAttributesEnabled = 4555 + PropertyGuestInitiateFileTransferFromGuestEnabled = 4556 + PropertyGuestInitiateFileTransferToGuestEnabled = 4557 +) + +type Property struct { + header struct { + ID int32 + Kind int32 + Length int32 + } + + data struct { + Int32 int32 + String string + Bool uint8 + Int64 int64 + Blob []byte + } +} + +var int32Size int32 + +func init() { + var i int32 + int32Size = int32(binary.Size(&i)) +} + +type PropertyList []*Property + +func NewInt32Property(ID int32, val int32) *Property { + p := new(Property) + p.header.ID = ID + p.header.Kind = vixPropertyTypeInt32 + p.header.Length = int32Size + p.data.Int32 = val + return p +} + +func NewStringProperty(ID int32, val string) *Property { + p := new(Property) + p.header.ID = ID + p.header.Kind = vixPropertyTypeString + p.header.Length = int32(len(val) + 1) + p.data.String = val + return p +} + +func NewBoolProperty(ID int32, val bool) *Property { + p := new(Property) + p.header.ID = ID + p.header.Kind = vixPropertyTypeBool + p.header.Length = 1 + if val { + p.data.Bool = 1 + } + return p +} + +func NewInt64Property(ID int32, val int64) *Property { + p := new(Property) + p.header.ID = ID + p.header.Kind = vixPropertyTypeInt64 + p.header.Length = int32Size * 2 + p.data.Int64 = val + return p +} + +func NewBlobProperty(ID int32, val []byte) *Property { + p := new(Property) + p.header.ID = ID + p.header.Kind = vixPropertyTypeBlob + p.header.Length = int32(len(val)) + p.data.Blob = val + return p +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (p *Property) MarshalBinary() ([]byte, error) { + buf := new(bytes.Buffer) + + // #nosec: Errors unhandled + _ = binary.Write(buf, binary.LittleEndian, &p.header) + + switch p.header.Kind { + case vixPropertyTypeBool: + // #nosec: Errors unhandled + _ = binary.Write(buf, binary.LittleEndian, p.data.Bool) + case vixPropertyTypeInt32: + // #nosec: Errors unhandled + _ = binary.Write(buf, binary.LittleEndian, p.data.Int32) + case vixPropertyTypeInt64: + // #nosec: Errors unhandled + _ = binary.Write(buf, binary.LittleEndian, p.data.Int64) + case vixPropertyTypeString: + // #nosec: Errors unhandled + _, _ = buf.WriteString(p.data.String) + // #nosec: Errors unhandled + _ = buf.WriteByte(0) + case vixPropertyTypeBlob: + // #nosec: Errors unhandled + _, _ = buf.Write(p.data.Blob) + } + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (p *Property) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, &p.header) + if err != nil { + return err + } + + switch p.header.Kind { + case vixPropertyTypeBool: + return binary.Read(buf, binary.LittleEndian, &p.data.Bool) + case vixPropertyTypeInt32: + return binary.Read(buf, binary.LittleEndian, &p.data.Int32) + case vixPropertyTypeInt64: + return binary.Read(buf, binary.LittleEndian, &p.data.Int64) + case vixPropertyTypeString: + s := make([]byte, p.header.Length) + if _, err := buf.Read(s); err != nil { + return err + } + + p.data.String = string(bytes.TrimRight(s, "\x00")) + case vixPropertyTypeBlob: + p.data.Blob = make([]byte, p.header.Length) + if _, err := buf.Read(p.data.Blob); err != nil { + return err + } + default: + return errors.New("VIX_E_UNRECOGNIZED_PROPERTY") + } + + return nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (l *PropertyList) UnmarshalBinary(data []byte) error { + headerSize := int32Size * 3 + + for { + p := new(Property) + + err := p.UnmarshalBinary(data) + if err != nil { + return err + } + + *l = append(*l, p) + + offset := headerSize + p.header.Length + data = data[offset:] + + if len(data) == 0 { + return nil + } + } +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (l *PropertyList) MarshalBinary() ([]byte, error) { + var buf bytes.Buffer + + for _, p := range *l { + // #nosec: Errors unhandled + b, _ := p.MarshalBinary() + // #nosec: Errors unhandled + _, _ = buf.Write(b) + } + + return buf.Bytes(), nil +} diff --git a/vendor/github.com/vmware/govmomi/toolbox/vix/protocol.go b/vendor/github.com/vmware/govmomi/toolbox/vix/protocol.go new file mode 100644 index 00000000..c15c4598 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/toolbox/vix/protocol.go @@ -0,0 +1,847 @@ +/* +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 vix + +import ( + "bytes" + "encoding/base64" + "encoding/binary" + "fmt" + "os" + "os/exec" + "syscall" +) + +const ( + CommandMagicWord = 0xd00d0001 + + CommandGetToolsState = 62 + + CommandStartProgram = 185 + CommandListProcessesEx = 186 + CommandReadEnvVariables = 187 + CommandTerminateProcess = 193 + + CommandCreateDirectoryEx = 178 + CommandMoveGuestFileEx = 179 + CommandMoveGuestDirectory = 180 + CommandCreateTemporaryFileEx = 181 + CommandCreateTemporaryDirectory = 182 + CommandSetGuestFileAttributes = 183 + CommandDeleteGuestFileEx = 194 + CommandDeleteGuestDirectoryEx = 195 + + CommandListFiles = 177 + HgfsSendPacketCommand = 84 + CommandInitiateFileTransferFromGuest = 188 + CommandInitiateFileTransferToGuest = 189 + + // VIX_USER_CREDENTIAL_NAME_PASSWORD + UserCredentialTypeNamePassword = 1 + + // VIX_E_* constants from vix.h + OK = 0 + Fail = 1 + InvalidArg = 3 + FileNotFound = 4 + FileAlreadyExists = 12 + FileAccessError = 13 + AuthenticationFail = 35 + + UnrecognizedCommandInGuest = 3025 + InvalidMessageHeader = 10000 + InvalidMessageBody = 10001 + NotAFile = 20001 + NotADirectory = 20002 + NoSuchProcess = 20003 + DirectoryNotEmpty = 20006 + + // VIX_COMMAND_* constants from Commands.h + CommandGuestReturnsBinary = 0x80 + + // VIX_FILE_ATTRIBUTES_ constants from vix.h + FileAttributesDirectory = 0x0001 + FileAttributesSymlink = 0x0002 +) + +// SetGuestFileAttributes flags as defined in vixOpenSource.h +const ( + FileAttributeSetAccessDate = 0x0001 + FileAttributeSetModifyDate = 0x0002 + FileAttributeSetReadonly = 0x0004 + FileAttributeSetHidden = 0x0008 + FileAttributeSetUnixOwnerid = 0x0010 + FileAttributeSetUnixGroupid = 0x0020 + FileAttributeSetUnixPermissions = 0x0040 +) + +type Error int + +func (err Error) Error() string { + return fmt.Sprintf("vix error=%d", err) +} + +// ErrorCode does its best to map the given error to a VIX error code. +// See also: Vix_TranslateErrno +func ErrorCode(err error) int { + switch t := err.(type) { + case Error: + return int(t) + case *os.PathError: + if errno, ok := t.Err.(syscall.Errno); ok { + switch errno { + case syscall.ENOTEMPTY: + return DirectoryNotEmpty + } + } + case *exec.Error: + if t.Err == exec.ErrNotFound { + return FileNotFound + } + } + + switch { + case os.IsNotExist(err): + return FileNotFound + case os.IsExist(err): + return FileAlreadyExists + case os.IsPermission(err): + return FileAccessError + default: + return Fail + } +} + +type Header struct { + Magic uint32 + MessageVersion uint16 + + TotalMessageLength uint32 + HeaderLength uint32 + BodyLength uint32 + CredentialLength uint32 + + CommonFlags uint8 +} + +type CommandRequestHeader struct { + Header + + OpCode uint32 + RequestFlags uint32 + + TimeOut uint32 + + Cookie uint64 + ClientHandleID uint32 + + UserCredentialType uint32 +} + +type StartProgramRequest struct { + CommandRequestHeader + + Body struct { + StartMinimized uint8 + ProgramPathLength uint32 + ArgumentsLength uint32 + WorkingDirLength uint32 + NumEnvVars uint32 + EnvVarLength uint32 + } + + ProgramPath string + Arguments string + WorkingDir string + EnvVars []string +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *StartProgramRequest) MarshalBinary() ([]byte, error) { + var env bytes.Buffer + + if n := len(r.EnvVars); n != 0 { + for _, e := range r.EnvVars { + _, _ = env.Write([]byte(e)) + _ = env.WriteByte(0) + } + r.Body.NumEnvVars = uint32(n) + r.Body.EnvVarLength = uint32(env.Len()) + } + + var fields []string + + add := func(s string, l *uint32) { + if n := len(s); n != 0 { + *l = uint32(n) + 1 + fields = append(fields, s) + } + } + + add(r.ProgramPath, &r.Body.ProgramPathLength) + add(r.Arguments, &r.Body.ArgumentsLength) + add(r.WorkingDir, &r.Body.WorkingDirLength) + + buf := new(bytes.Buffer) + + _ = binary.Write(buf, binary.LittleEndian, &r.Body) + + for _, val := range fields { + _, _ = buf.Write([]byte(val)) + _ = buf.WriteByte(0) + } + + if r.Body.EnvVarLength != 0 { + _, _ = buf.Write(env.Bytes()) + } + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *StartProgramRequest) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, &r.Body) + if err != nil { + return err + } + + fields := []struct { + len uint32 + val *string + }{ + {r.Body.ProgramPathLength, &r.ProgramPath}, + {r.Body.ArgumentsLength, &r.Arguments}, + {r.Body.WorkingDirLength, &r.WorkingDir}, + } + + for _, field := range fields { + if field.len == 0 { + continue + } + + x := buf.Next(int(field.len)) + *field.val = string(bytes.TrimRight(x, "\x00")) + } + + for i := 0; i < int(r.Body.NumEnvVars); i++ { + env, rerr := buf.ReadString(0) + if rerr != nil { + return rerr + } + + env = env[:len(env)-1] // discard NULL terminator + r.EnvVars = append(r.EnvVars, env) + } + + return nil +} + +type KillProcessRequest struct { + CommandRequestHeader + + Body struct { + Pid int64 + Options uint32 + } +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *KillProcessRequest) MarshalBinary() ([]byte, error) { + buf := new(bytes.Buffer) + + _ = binary.Write(buf, binary.LittleEndian, &r.Body) + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *KillProcessRequest) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + return binary.Read(buf, binary.LittleEndian, &r.Body) +} + +type ListProcessesRequest struct { + CommandRequestHeader + + Body struct { + Key uint32 + Offset uint32 + NumPids uint32 + } + + Pids []int64 +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *ListProcessesRequest) MarshalBinary() ([]byte, error) { + r.Body.NumPids = uint32(len(r.Pids)) + + buf := new(bytes.Buffer) + + _ = binary.Write(buf, binary.LittleEndian, &r.Body) + + for _, pid := range r.Pids { + _ = binary.Write(buf, binary.LittleEndian, &pid) + } + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *ListProcessesRequest) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, &r.Body) + if err != nil { + return err + } + + r.Pids = make([]int64, r.Body.NumPids) + + for i := uint32(0); i < r.Body.NumPids; i++ { + err := binary.Read(buf, binary.LittleEndian, &r.Pids[i]) + if err != nil { + return err + } + } + + return nil +} + +type ReadEnvironmentVariablesRequest struct { + CommandRequestHeader + + Body struct { + NumNames uint32 + NamesLength uint32 + } + + Names []string +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *ReadEnvironmentVariablesRequest) MarshalBinary() ([]byte, error) { + var env bytes.Buffer + + if n := len(r.Names); n != 0 { + for _, e := range r.Names { + _, _ = env.Write([]byte(e)) + _ = env.WriteByte(0) + } + r.Body.NumNames = uint32(n) + r.Body.NamesLength = uint32(env.Len()) + } + + buf := new(bytes.Buffer) + + _ = binary.Write(buf, binary.LittleEndian, &r.Body) + + if r.Body.NamesLength != 0 { + _, _ = buf.Write(env.Bytes()) + } + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *ReadEnvironmentVariablesRequest) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, &r.Body) + if err != nil { + return err + } + + for i := 0; i < int(r.Body.NumNames); i++ { + env, rerr := buf.ReadString(0) + if rerr != nil { + return rerr + } + + env = env[:len(env)-1] // discard NULL terminator + r.Names = append(r.Names, env) + } + + return nil +} + +type CreateTempFileRequest struct { + CommandRequestHeader + + Body struct { + Options int32 + FilePrefixLength uint32 + FileSuffixLength uint32 + DirectoryPathLength uint32 + PropertyListLength uint32 + } + + FilePrefix string + FileSuffix string + DirectoryPath string +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *CreateTempFileRequest) MarshalBinary() ([]byte, error) { + var fields []string + + add := func(s string, l *uint32) { + *l = uint32(len(s)) // NOTE: NULL byte is not included in the length fields on the wire + fields = append(fields, s) + } + + add(r.FilePrefix, &r.Body.FilePrefixLength) + add(r.FileSuffix, &r.Body.FileSuffixLength) + add(r.DirectoryPath, &r.Body.DirectoryPathLength) + + buf := new(bytes.Buffer) + + _ = binary.Write(buf, binary.LittleEndian, &r.Body) + + for _, val := range fields { + _, _ = buf.Write([]byte(val)) + _ = buf.WriteByte(0) + } + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *CreateTempFileRequest) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, &r.Body) + if err != nil { + return err + } + + fields := []struct { + len uint32 + val *string + }{ + {r.Body.FilePrefixLength, &r.FilePrefix}, + {r.Body.FileSuffixLength, &r.FileSuffix}, + {r.Body.DirectoryPathLength, &r.DirectoryPath}, + } + + for _, field := range fields { + field.len++ // NOTE: NULL byte is not included in the length fields on the wire + + x := buf.Next(int(field.len)) + *field.val = string(bytes.TrimRight(x, "\x00")) + } + + return nil +} + +type FileRequest struct { + CommandRequestHeader + + Body struct { + FileOptions int32 + GuestPathNameLength uint32 + } + + GuestPathName string +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *FileRequest) MarshalBinary() ([]byte, error) { + buf := new(bytes.Buffer) + + r.Body.GuestPathNameLength = uint32(len(r.GuestPathName)) + + _ = binary.Write(buf, binary.LittleEndian, &r.Body) + + _, _ = buf.WriteString(r.GuestPathName) + _ = buf.WriteByte(0) + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *FileRequest) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, &r.Body) + if err != nil { + return err + } + + name := buf.Next(int(r.Body.GuestPathNameLength)) + r.GuestPathName = string(bytes.TrimRight(name, "\x00")) + + return nil +} + +type DirRequest struct { + CommandRequestHeader + + Body struct { + FileOptions int32 + GuestPathNameLength uint32 + FilePropertiesLength uint32 + Recursive bool + } + + GuestPathName string +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *DirRequest) MarshalBinary() ([]byte, error) { + buf := new(bytes.Buffer) + + r.Body.GuestPathNameLength = uint32(len(r.GuestPathName)) + + _ = binary.Write(buf, binary.LittleEndian, &r.Body) + + _, _ = buf.WriteString(r.GuestPathName) + _ = buf.WriteByte(0) + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *DirRequest) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, &r.Body) + if err != nil { + return err + } + + name := buf.Next(int(r.Body.GuestPathNameLength)) + r.GuestPathName = string(bytes.TrimRight(name, "\x00")) + + return nil +} + +type RenameFileRequest struct { + CommandRequestHeader + + Body struct { + CopyFileOptions int32 + OldPathNameLength uint32 + NewPathNameLength uint32 + FilePropertiesLength uint32 + Overwrite bool + } + + OldPathName string + NewPathName string +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *RenameFileRequest) MarshalBinary() ([]byte, error) { + var fields []string + + add := func(s string, l *uint32) { + *l = uint32(len(s)) // NOTE: NULL byte is not included in the length fields on the wire + fields = append(fields, s) + } + + add(r.OldPathName, &r.Body.OldPathNameLength) + add(r.NewPathName, &r.Body.NewPathNameLength) + + buf := new(bytes.Buffer) + + _ = binary.Write(buf, binary.LittleEndian, &r.Body) + + for _, val := range fields { + _, _ = buf.Write([]byte(val)) + _ = buf.WriteByte(0) + } + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *RenameFileRequest) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, &r.Body) + if err != nil { + return err + } + + fields := []struct { + len uint32 + val *string + }{ + {r.Body.OldPathNameLength, &r.OldPathName}, + {r.Body.NewPathNameLength, &r.NewPathName}, + } + + for _, field := range fields { + field.len++ // NOTE: NULL byte is not included in the length fields on the wire + + x := buf.Next(int(field.len)) + *field.val = string(bytes.TrimRight(x, "\x00")) + } + + return nil +} + +type ListFilesRequest struct { + CommandRequestHeader + + Body struct { + FileOptions int32 + GuestPathNameLength uint32 + PatternLength uint32 + Index int32 + MaxResults int32 + Offset uint64 + } + + GuestPathName string + Pattern string +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *ListFilesRequest) MarshalBinary() ([]byte, error) { + var fields []string + + add := func(s string, l *uint32) { + if n := len(s); n != 0 { + *l = uint32(n) + 1 + fields = append(fields, s) + } + } + + add(r.GuestPathName, &r.Body.GuestPathNameLength) + add(r.Pattern, &r.Body.PatternLength) + + buf := new(bytes.Buffer) + + _ = binary.Write(buf, binary.LittleEndian, &r.Body) + + for _, val := range fields { + _, _ = buf.Write([]byte(val)) + _ = buf.WriteByte(0) + } + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *ListFilesRequest) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, &r.Body) + if err != nil { + return err + } + + fields := []struct { + len uint32 + val *string + }{ + {r.Body.GuestPathNameLength, &r.GuestPathName}, + {r.Body.PatternLength, &r.Pattern}, + } + + for _, field := range fields { + if field.len == 0 { + continue + } + + x := buf.Next(int(field.len)) + *field.val = string(bytes.TrimRight(x, "\x00")) + } + + return nil +} + +type SetGuestFileAttributesRequest struct { + CommandRequestHeader + + Body struct { + FileOptions int32 + AccessTime int64 + ModificationTime int64 + OwnerID int32 + GroupID int32 + Permissions int32 + Hidden bool + ReadOnly bool + GuestPathNameLength uint32 + } + + GuestPathName string +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *SetGuestFileAttributesRequest) MarshalBinary() ([]byte, error) { + buf := new(bytes.Buffer) + + r.Body.GuestPathNameLength = uint32(len(r.GuestPathName)) + + _ = binary.Write(buf, binary.LittleEndian, &r.Body) + + _, _ = buf.WriteString(r.GuestPathName) + _ = buf.WriteByte(0) + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *SetGuestFileAttributesRequest) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, &r.Body) + if err != nil { + return err + } + + name := buf.Next(int(r.Body.GuestPathNameLength)) + r.GuestPathName = string(bytes.TrimRight(name, "\x00")) + + return nil +} + +func (r *SetGuestFileAttributesRequest) IsSet(opt int32) bool { + return r.Body.FileOptions&opt == opt +} + +type CommandHgfsSendPacket struct { + CommandRequestHeader + + Body struct { + PacketSize uint32 + Timeout int32 + } + + Packet []byte +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *CommandHgfsSendPacket) MarshalBinary() ([]byte, error) { + buf := new(bytes.Buffer) + + _ = binary.Write(buf, binary.LittleEndian, &r.Body) + + _, _ = buf.Write(r.Packet) + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *CommandHgfsSendPacket) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, &r.Body) + if err != nil { + return err + } + + r.Packet = buf.Next(int(r.Body.PacketSize)) + + return nil +} + +type InitiateFileTransferToGuestRequest struct { + CommandRequestHeader + + Body struct { + Options int32 + GuestPathNameLength uint32 + Overwrite bool + } + + GuestPathName string +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface +func (r *InitiateFileTransferToGuestRequest) MarshalBinary() ([]byte, error) { + buf := new(bytes.Buffer) + + r.Body.GuestPathNameLength = uint32(len(r.GuestPathName)) + + _ = binary.Write(buf, binary.LittleEndian, &r.Body) + + _, _ = buf.WriteString(r.GuestPathName) + _ = buf.WriteByte(0) + + return buf.Bytes(), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface +func (r *InitiateFileTransferToGuestRequest) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(data) + + err := binary.Read(buf, binary.LittleEndian, &r.Body) + if err != nil { + return err + } + + name := buf.Next(int(r.Body.GuestPathNameLength)) + r.GuestPathName = string(bytes.TrimRight(name, "\x00")) + + return nil +} + +type UserCredentialNamePassword struct { + Body struct { + NameLength uint32 + PasswordLength uint32 + } + + Name string + Password string +} + +func (c *UserCredentialNamePassword) UnmarshalBinary(data []byte) error { + buf := bytes.NewBuffer(bytes.TrimRight(data, "\x00")) + + err := binary.Read(buf, binary.LittleEndian, &c.Body) + if err != nil { + return err + } + + str, err := base64.StdEncoding.DecodeString(string(buf.Bytes())) + if err != nil { + return err + } + + c.Name = string(str[0:c.Body.NameLength]) + c.Password = string(str[c.Body.NameLength+1 : len(str)-1]) + + return nil +} + +func (c *UserCredentialNamePassword) MarshalBinary() ([]byte, error) { + buf := new(bytes.Buffer) + + c.Body.NameLength = uint32(len(c.Name)) + c.Body.PasswordLength = uint32(len(c.Password)) + + _ = binary.Write(buf, binary.LittleEndian, &c.Body) + + src := append([]byte(c.Name+"\x00"), []byte(c.Password+"\x00")...) + + enc := base64.StdEncoding + pwd := make([]byte, enc.EncodedLen(len(src))) + enc.Encode(pwd, src) + _, _ = buf.Write(pwd) + _ = buf.WriteByte(0) + + return buf.Bytes(), nil +} diff --git a/vendor/github.com/vmware/govmomi/units/size.go b/vendor/github.com/vmware/govmomi/units/size.go index 48208364..f4ee3fcf 100644 --- a/vendor/github.com/vmware/govmomi/units/size.go +++ b/vendor/github.com/vmware/govmomi/units/size.go @@ -53,6 +53,26 @@ func (b ByteSize) String() string { return fmt.Sprintf("%dB", b) } +type FileSize int64 + +func (b FileSize) String() string { + switch { + case b >= EB: + return fmt.Sprintf("%.1fE", float32(b)/EB) + case b >= PB: + return fmt.Sprintf("%.1fP", float32(b)/PB) + case b >= TB: + return fmt.Sprintf("%.1fT", float32(b)/TB) + case b >= GB: + return fmt.Sprintf("%.1fG", float32(b)/GB) + case b >= MB: + return fmt.Sprintf("%.1fM", float32(b)/MB) + case b >= KB: + return fmt.Sprintf("%.1fK", float32(b)/KB) + } + return fmt.Sprintf("%d", b) +} + var bytesRegexp = regexp.MustCompile(`^(?i)(\d+)([BKMGTPE]?)(ib|b)?$`) func (b *ByteSize) Set(s string) error { diff --git a/vendor/github.com/vmware/govmomi/vcsim/main.go b/vendor/github.com/vmware/govmomi/vcsim/main.go new file mode 100644 index 00000000..b2aa6a10 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vcsim/main.go @@ -0,0 +1,124 @@ +/* +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 main + +import ( + "crypto/tls" + "flag" + "fmt" + "log" + "os" + "os/signal" + "runtime" + "syscall" + + "github.com/google/uuid" + "github.com/vmware/govmomi/simulator" + "github.com/vmware/govmomi/simulator/esx" +) + +func main() { + model := simulator.VPX() + + flag.IntVar(&model.Datacenter, "dc", model.Datacenter, "Number of datacenters") + flag.IntVar(&model.Cluster, "cluster", model.Cluster, "Number of clusters") + flag.IntVar(&model.ClusterHost, "host", model.ClusterHost, "Number of hosts per cluster") + flag.IntVar(&model.Host, "standalone-host", model.Host, "Number of standalone hosts") + flag.IntVar(&model.Datastore, "ds", model.Datastore, "Number of local datastores") + flag.IntVar(&model.Machine, "vm", model.Machine, "Number of virtual machines per resource pool") + flag.IntVar(&model.Pool, "pool", model.Pool, "Number of resource pools per compute resource") + flag.IntVar(&model.App, "app", model.App, "Number of virtual apps per compute resource") + flag.IntVar(&model.Pod, "pod", model.Pod, "Number of storage pods per datacenter") + flag.IntVar(&model.Portgroup, "pg", model.Portgroup, "Number of port groups") + flag.IntVar(&model.Folder, "folder", model.Folder, "Number of folders") + + isESX := flag.Bool("esx", false, "Simulate standalone ESX") + isTLS := flag.Bool("tls", true, "Enable TLS") + cert := flag.String("tlscert", "", "Path to TLS certificate file") + key := flag.String("tlskey", "", "Path to TLS key file") + env := flag.String("E", "-", "Output vcsim variables to the given fifo or stdout") + flag.BoolVar(&simulator.Trace, "trace", simulator.Trace, "Trace SOAP to stderr") + + flag.Parse() + + switch flag.Arg(0) { + case "uuidgen": // util-linux not installed on Travis CI + fmt.Println(uuid.New().String()) + return + } + + var err error + out := os.Stdout + + if *env != "-" { + out, err = os.OpenFile(*env, os.O_WRONLY, 0) + if err != nil { + log.Fatal(err) + } + } + + f := flag.Lookup("httptest.serve") + if f.Value.String() == "" { + // #nosec: Errors unhandled + _ = f.Value.Set("127.0.0.1:8989") + } + + if *isESX { + opts := model + model = simulator.ESX() + // Preserve options that also apply to ESX + model.Datastore = opts.Datastore + model.Machine = opts.Machine + } + + tag := " (govmomi simulator)" + model.ServiceContent.About.Name += tag + model.ServiceContent.About.OsType = runtime.GOOS + "-" + runtime.GOARCH + + esx.HostSystem.Summary.Hardware.Vendor += tag + + err = model.Create() + if err != nil { + log.Fatal(err) + } + + if *isTLS { + model.Service.TLS = new(tls.Config) + if *cert != "" { + c, err := tls.LoadX509KeyPair(*cert, *key) + if err != nil { + log.Fatal(err) + } + + model.Service.TLS.Certificates = []tls.Certificate{c} + } + } + + s := model.Service.NewServer() + + fmt.Fprintf(out, "export GOVC_URL=%s GOVC_SIM_PID=%d\n", s.URL, os.Getpid()) + if out != os.Stdout { + _ = out.Close() + } + + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) + + <-sig + + model.Remove() +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/bypass.go index 565bf589..d42a0bc4 100644 --- a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/bypass.go +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/bypass.go @@ -13,9 +13,10 @@ // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // NOTE: Due to the following build constraints, this file will only be compiled -// when the code is not running on Google App Engine and "-tags disableunsafe" -// is not added to the go build command line. -// +build !appengine,!disableunsafe +// when the code is not running on Google App Engine, compiled by GopherJS, and +// "-tags safe" is not added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// +build !js,!appengine,!safe,!disableunsafe package spew diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go index 457e4123..e47a4e79 100644 --- a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go @@ -13,9 +13,10 @@ // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // NOTE: Due to the following build constraints, this file will only be compiled -// when either the code is running on Google App Engine or "-tags disableunsafe" -// is added to the go build command line. -// +build appengine disableunsafe +// when the code is running on Google App Engine, compiled by GopherJS, or +// "-tags safe" is added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// +build js appengine safe disableunsafe package spew diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/config.go b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/config.go index ee1ab07b..9db83c66 100644 --- a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/config.go +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/config.go @@ -64,9 +64,18 @@ type ConfigState struct { // inside these interface methods. As a result, this option relies on // access to the unsafe package, so it will not have any effect when // running in environments without access to the unsafe package such as - // Google App Engine or with the "disableunsafe" build tag specified. + // Google App Engine or with the "safe" build tag specified. DisablePointerMethods bool + // DisablePointerAddresses specifies whether to disable the printing of + // pointer addresses. This is useful when diffing data structures in tests. + DisablePointerAddresses bool + + // DisableCapacities specifies whether to disable the printing of capacities + // for arrays, slices, maps and channels. This is useful when diffing + // data structures in tests. + DisableCapacities bool + // ContinueOnMethod specifies whether or not recursion should continue once // a custom error or Stringer interface is invoked. The default, false, // means it will print the results of invoking the custom error or Stringer diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/dump.go index a0ff95e2..95f9dbfe 100644 --- a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/dump.go +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-spew/spew/dump.go @@ -129,7 +129,7 @@ func (d *dumpState) dumpPtr(v reflect.Value) { d.w.Write(closeParenBytes) // Display pointer information. - if len(pointerChain) > 0 { + if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { d.w.Write(openParenBytes) for i, addr := range pointerChain { if i > 0 { @@ -282,13 +282,13 @@ func (d *dumpState) dump(v reflect.Value) { case reflect.Map, reflect.String: valueLen = v.Len() } - if valueLen != 0 || valueCap != 0 { + if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { d.w.Write(openParenBytes) if valueLen != 0 { d.w.Write(lenEqualsBytes) printInt(d.w, int64(valueLen), 10) } - if valueCap != 0 { + if !d.cs.DisableCapacities && valueCap != 0 { if valueLen != 0 { d.w.Write(spaceBytes) } diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/decode.go b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/decode.go new file mode 100644 index 00000000..6f37f76b --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/decode.go @@ -0,0 +1,918 @@ +/* + * Copyright (c) 2012-2014 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package xdr + +import ( + "fmt" + "io" + "math" + "reflect" + "strconv" + "time" +) + +var ( + errMaxSlice = "data exceeds max slice limit" + errIODecode = "%s while decoding %d bytes" +) + +/* +Unmarshal parses XDR-encoded data into the value pointed to by v reading from +reader r and returning the total number of bytes read. An addressable pointer +must be provided since Unmarshal needs to both store the result of the decode as +well as obtain target type information. Unmarhsal traverses v recursively and +automatically indirects pointers through arbitrary depth, allocating them as +necessary, to decode the data into the underlying value pointed to. + +Unmarshal uses reflection to determine the type of the concrete value contained +by v and performs a mapping of underlying XDR types to Go types as follows: + + Go Type <- XDR Type + -------------------- + int8, int16, int32, int <- XDR Integer + uint8, uint16, uint32, uint <- XDR Unsigned Integer + int64 <- XDR Hyper Integer + uint64 <- XDR Unsigned Hyper Integer + bool <- XDR Boolean + float32 <- XDR Floating-Point + float64 <- XDR Double-Precision Floating-Point + string <- XDR String + byte <- XDR Integer + []byte <- XDR Variable-Length Opaque Data + [#]byte <- XDR Fixed-Length Opaque Data + [] <- XDR Variable-Length Array + [#] <- XDR Fixed-Length Array + struct <- XDR Structure + map <- XDR Variable-Length Array of two-element XDR Structures + time.Time <- XDR String encoded with RFC3339 nanosecond precision + +Notes and Limitations: + + * Automatic unmarshalling of variable and fixed-length arrays of uint8s + requires a special struct tag `xdropaque:"false"` since byte slices + and byte arrays are assumed to be opaque data and byte is a Go alias + for uint8 thus indistinguishable under reflection + * Cyclic data structures are not supported and will result in infinite + loops + +If any issues are encountered during the unmarshalling process, an +UnmarshalError is returned with a human readable description as well as +an ErrorCode value for further inspection from sophisticated callers. Some +potential issues are unsupported Go types, attempting to decode a value which is +too large to fit into a specified Go type, and exceeding max slice limitations. +*/ +func Unmarshal(r io.Reader, v interface{}) (int, error) { + d := Decoder{r: r} + return d.Decode(v) +} + +// UnmarshalLimited is identical to Unmarshal but it sets maxReadSize in order +// to cap reads. +func UnmarshalLimited(r io.Reader, v interface{}, maxSize uint) (int, error) { + d := Decoder{r: r, maxReadSize: maxSize} + return d.Decode(v) +} + +// A Decoder wraps an io.Reader that is expected to provide an XDR-encoded byte +// stream and provides several exposed methods to manually decode various XDR +// primitives without relying on reflection. The NewDecoder function can be +// used to get a new Decoder directly. +// +// Typically, Unmarshal should be used instead of manual decoding. A Decoder +// is exposed so it is possible to perform manual decoding should it be +// necessary in complex scenarios where automatic reflection-based decoding +// won't work. +type Decoder struct { + r io.Reader + + // maxReadSize is the default maximum bytes an element can contain. 0 + // is unlimited and provides backwards compatability. Setting it to a + // non-zero value caps reads. + maxReadSize uint +} + +// DecodeInt treats the next 4 bytes as an XDR encoded integer and returns the +// result as an int32 along with the number of bytes actually read. +// +// An UnmarshalError is returned if there are insufficient bytes remaining. +// +// Reference: +// RFC Section 4.1 - Integer +// 32-bit big-endian signed integer in range [-2147483648, 2147483647] +func (d *Decoder) DecodeInt() (int32, int, error) { + var buf [4]byte + n, err := io.ReadFull(d.r, buf[:]) + if err != nil { + msg := fmt.Sprintf(errIODecode, err.Error(), 4) + err := unmarshalError("DecodeInt", ErrIO, msg, buf[:n], err) + return 0, n, err + } + + rv := int32(buf[3]) | int32(buf[2])<<8 | + int32(buf[1])<<16 | int32(buf[0])<<24 + return rv, n, nil +} + +// DecodeUint treats the next 4 bytes as an XDR encoded unsigned integer and +// returns the result as a uint32 along with the number of bytes actually read. +// +// An UnmarshalError is returned if there are insufficient bytes remaining. +// +// Reference: +// RFC Section 4.2 - Unsigned Integer +// 32-bit big-endian unsigned integer in range [0, 4294967295] +func (d *Decoder) DecodeUint() (uint32, int, error) { + var buf [4]byte + n, err := io.ReadFull(d.r, buf[:]) + if err != nil { + msg := fmt.Sprintf(errIODecode, err.Error(), 4) + err := unmarshalError("DecodeUint", ErrIO, msg, buf[:n], err) + return 0, n, err + } + + rv := uint32(buf[3]) | uint32(buf[2])<<8 | + uint32(buf[1])<<16 | uint32(buf[0])<<24 + return rv, n, nil +} + +// DecodeEnum treats the next 4 bytes as an XDR encoded enumeration value and +// returns the result as an int32 after verifying that the value is in the +// provided map of valid values. It also returns the number of bytes actually +// read. +// +// An UnmarshalError is returned if there are insufficient bytes remaining or +// the parsed enumeration value is not one of the provided valid values. +// +// Reference: +// RFC Section 4.3 - Enumeration +// Represented as an XDR encoded signed integer +func (d *Decoder) DecodeEnum(validEnums map[int32]bool) (int32, int, error) { + val, n, err := d.DecodeInt() + if err != nil { + return 0, n, err + } + + if !validEnums[val] { + err := unmarshalError("DecodeEnum", ErrBadEnumValue, + "invalid enum", val, nil) + return 0, n, err + } + return val, n, nil +} + +// DecodeBool treats the next 4 bytes as an XDR encoded boolean value and +// returns the result as a bool along with the number of bytes actually read. +// +// An UnmarshalError is returned if there are insufficient bytes remaining or +// the parsed value is not a 0 or 1. +// +// Reference: +// RFC Section 4.4 - Boolean +// Represented as an XDR encoded enumeration where 0 is false and 1 is true +func (d *Decoder) DecodeBool() (bool, int, error) { + val, n, err := d.DecodeInt() + if err != nil { + return false, n, err + } + switch val { + case 0: + return false, n, nil + case 1: + return true, n, nil + } + + err = unmarshalError("DecodeBool", ErrBadEnumValue, "bool not 0 or 1", + val, nil) + return false, n, err +} + +// DecodeHyper treats the next 8 bytes as an XDR encoded hyper value and +// returns the result as an int64 along with the number of bytes actually read. +// +// An UnmarshalError is returned if there are insufficient bytes remaining. +// +// Reference: +// RFC Section 4.5 - Hyper Integer +// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] +func (d *Decoder) DecodeHyper() (int64, int, error) { + var buf [8]byte + n, err := io.ReadFull(d.r, buf[:]) + if err != nil { + msg := fmt.Sprintf(errIODecode, err.Error(), 8) + err := unmarshalError("DecodeHyper", ErrIO, msg, buf[:n], err) + return 0, n, err + } + + rv := int64(buf[7]) | int64(buf[6])<<8 | + int64(buf[5])<<16 | int64(buf[4])<<24 | + int64(buf[3])<<32 | int64(buf[2])<<40 | + int64(buf[1])<<48 | int64(buf[0])<<56 + return rv, n, err +} + +// DecodeUhyper treats the next 8 bytes as an XDR encoded unsigned hyper value +// and returns the result as a uint64 along with the number of bytes actually +// read. +// +// An UnmarshalError is returned if there are insufficient bytes remaining. +// +// Reference: +// RFC Section 4.5 - Unsigned Hyper Integer +// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] +func (d *Decoder) DecodeUhyper() (uint64, int, error) { + var buf [8]byte + n, err := io.ReadFull(d.r, buf[:]) + if err != nil { + msg := fmt.Sprintf(errIODecode, err.Error(), 8) + err := unmarshalError("DecodeUhyper", ErrIO, msg, buf[:n], err) + return 0, n, err + } + + rv := uint64(buf[7]) | uint64(buf[6])<<8 | + uint64(buf[5])<<16 | uint64(buf[4])<<24 | + uint64(buf[3])<<32 | uint64(buf[2])<<40 | + uint64(buf[1])<<48 | uint64(buf[0])<<56 + return rv, n, nil +} + +// DecodeFloat treats the next 4 bytes as an XDR encoded floating point and +// returns the result as a float32 along with the number of bytes actually read. +// +// An UnmarshalError is returned if there are insufficient bytes remaining. +// +// Reference: +// RFC Section 4.6 - Floating Point +// 32-bit single-precision IEEE 754 floating point +func (d *Decoder) DecodeFloat() (float32, int, error) { + var buf [4]byte + n, err := io.ReadFull(d.r, buf[:]) + if err != nil { + msg := fmt.Sprintf(errIODecode, err.Error(), 4) + err := unmarshalError("DecodeFloat", ErrIO, msg, buf[:n], err) + return 0, n, err + } + + val := uint32(buf[3]) | uint32(buf[2])<<8 | + uint32(buf[1])<<16 | uint32(buf[0])<<24 + return math.Float32frombits(val), n, nil +} + +// DecodeDouble treats the next 8 bytes as an XDR encoded double-precision +// floating point and returns the result as a float64 along with the number of +// bytes actually read. +// +// An UnmarshalError is returned if there are insufficient bytes remaining. +// +// Reference: +// RFC Section 4.7 - Double-Precision Floating Point +// 64-bit double-precision IEEE 754 floating point +func (d *Decoder) DecodeDouble() (float64, int, error) { + var buf [8]byte + n, err := io.ReadFull(d.r, buf[:]) + if err != nil { + msg := fmt.Sprintf(errIODecode, err.Error(), 8) + err := unmarshalError("DecodeDouble", ErrIO, msg, buf[:n], err) + return 0, n, err + } + + val := uint64(buf[7]) | uint64(buf[6])<<8 | + uint64(buf[5])<<16 | uint64(buf[4])<<24 | + uint64(buf[3])<<32 | uint64(buf[2])<<40 | + uint64(buf[1])<<48 | uint64(buf[0])<<56 + return math.Float64frombits(val), n, nil +} + +// RFC Section 4.8 - Quadruple-Precision Floating Point +// 128-bit quadruple-precision floating point +// Not Implemented + +// DecodeFixedOpaque treats the next 'size' bytes as XDR encoded opaque data and +// returns the result as a byte slice along with the number of bytes actually +// read. +// +// An UnmarshalError is returned if there are insufficient bytes remaining to +// satisfy the passed size, including the necessary padding to make it a +// multiple of 4. +// +// Reference: +// RFC Section 4.9 - Fixed-Length Opaque Data +// Fixed-length uninterpreted data zero-padded to a multiple of four +func (d *Decoder) DecodeFixedOpaque(size int32) ([]byte, int, error) { + // Nothing to do if size is 0. + if size == 0 { + return nil, 0, nil + } + + pad := (4 - (size % 4)) % 4 + paddedSize := size + pad + if uint(paddedSize) > uint(math.MaxInt32) { + err := unmarshalError("DecodeFixedOpaque", ErrOverflow, + errMaxSlice, paddedSize, nil) + return nil, 0, err + } + + buf := make([]byte, paddedSize) + n, err := io.ReadFull(d.r, buf) + if err != nil { + msg := fmt.Sprintf(errIODecode, err.Error(), paddedSize) + err := unmarshalError("DecodeFixedOpaque", ErrIO, msg, buf[:n], + err) + return nil, n, err + } + return buf[0:size], n, nil +} + +// DecodeOpaque treats the next bytes as variable length XDR encoded opaque +// data and returns the result as a byte slice along with the number of bytes +// actually read. +// +// An UnmarshalError is returned if there are insufficient bytes remaining or +// the opaque data is larger than the max length of a Go slice. +// +// Reference: +// RFC Section 4.10 - Variable-Length Opaque Data +// Unsigned integer length followed by fixed opaque data of that length +func (d *Decoder) DecodeOpaque() ([]byte, int, error) { + dataLen, n, err := d.DecodeUint() + if err != nil { + return nil, n, err + } + if uint(dataLen) > uint(math.MaxInt32) || + (d.maxReadSize != 0 && uint(dataLen) > d.maxReadSize) { + err := unmarshalError("DecodeOpaque", ErrOverflow, errMaxSlice, + dataLen, nil) + return nil, n, err + } + + rv, n2, err := d.DecodeFixedOpaque(int32(dataLen)) + n += n2 + if err != nil { + return nil, n, err + } + return rv, n, nil +} + +// DecodeString treats the next bytes as a variable length XDR encoded string +// and returns the result as a string along with the number of bytes actually +// read. Character encoding is assumed to be UTF-8 and therefore ASCII +// compatible. If the underlying character encoding is not compatibile with +// this assumption, the data can instead be read as variable-length opaque data +// (DecodeOpaque) and manually converted as needed. +// +// An UnmarshalError is returned if there are insufficient bytes remaining or +// the string data is larger than the max length of a Go slice. +// +// Reference: +// RFC Section 4.11 - String +// Unsigned integer length followed by bytes zero-padded to a multiple of +// four +func (d *Decoder) DecodeString() (string, int, error) { + dataLen, n, err := d.DecodeUint() + if err != nil { + return "", n, err + } + if uint(dataLen) > uint(math.MaxInt32) || + (d.maxReadSize != 0 && uint(dataLen) > d.maxReadSize) { + err = unmarshalError("DecodeString", ErrOverflow, errMaxSlice, + dataLen, nil) + return "", n, err + } + + opaque, n2, err := d.DecodeFixedOpaque(int32(dataLen)) + n += n2 + if err != nil { + return "", n, err + } + return string(opaque), n, nil +} + +// decodeFixedArray treats the next bytes as a series of XDR encoded elements +// of the same type as the array represented by the reflection value and decodes +// each element into the passed array. The ignoreOpaque flag controls whether +// or not uint8 (byte) elements should be decoded individually or as a fixed +// sequence of opaque data. It returns the the number of bytes actually read. +// +// An UnmarshalError is returned if any issues are encountered while decoding +// the array elements. +// +// Reference: +// RFC Section 4.12 - Fixed-Length Array +// Individually XDR encoded array elements +func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, error) { + // Treat [#]byte (byte is alias for uint8) as opaque data unless + // ignored. + if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 { + data, n, err := d.DecodeFixedOpaque(int32(v.Len())) + if err != nil { + return n, err + } + reflect.Copy(v, reflect.ValueOf(data)) + return n, nil + } + + // Decode each array element. + var n int + for i := 0; i < v.Len(); i++ { + n2, err := d.decode(v.Index(i)) + n += n2 + if err != nil { + return n, err + } + } + return n, nil +} + +// decodeArray treats the next bytes as a variable length series of XDR encoded +// elements of the same type as the array represented by the reflection value. +// The number of elements is obtained by first decoding the unsigned integer +// element count. Then each element is decoded into the passed array. The +// ignoreOpaque flag controls whether or not uint8 (byte) elements should be +// decoded individually or as a variable sequence of opaque data. It returns +// the number of bytes actually read. +// +// An UnmarshalError is returned if any issues are encountered while decoding +// the array elements. +// +// Reference: +// RFC Section 4.13 - Variable-Length Array +// Unsigned integer length followed by individually XDR encoded array +// elements +func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool) (int, error) { + dataLen, n, err := d.DecodeUint() + if err != nil { + return n, err + } + if uint(dataLen) > uint(math.MaxInt32) || + (d.maxReadSize != 0 && uint(dataLen) > d.maxReadSize) { + err := unmarshalError("decodeArray", ErrOverflow, errMaxSlice, + dataLen, nil) + return n, err + } + + // Allocate storage for the slice elements (the underlying array) if + // existing slice does not have enough capacity. + sliceLen := int(dataLen) + if v.Cap() < sliceLen { + v.Set(reflect.MakeSlice(v.Type(), sliceLen, sliceLen)) + } + if v.Len() < sliceLen { + v.SetLen(sliceLen) + } + + // Treat []byte (byte is alias for uint8) as opaque data unless ignored. + if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 { + data, n2, err := d.DecodeFixedOpaque(int32(sliceLen)) + n += n2 + if err != nil { + return n, err + } + v.SetBytes(data) + return n, nil + } + + // Decode each slice element. + for i := 0; i < sliceLen; i++ { + n2, err := d.decode(v.Index(i)) + n += n2 + if err != nil { + return n, err + } + } + return n, nil +} + +// decodeStruct treats the next bytes as a series of XDR encoded elements +// of the same type as the exported fields of the struct represented by the +// passed reflection value. Pointers are automatically indirected and +// allocated as necessary. It returns the the number of bytes actually read. +// +// An UnmarshalError is returned if any issues are encountered while decoding +// the elements. +// +// Reference: +// RFC Section 4.14 - Structure +// XDR encoded elements in the order of their declaration in the struct +func (d *Decoder) decodeStruct(v reflect.Value) (int, error) { + var n int + var union string + vt := v.Type() + for i := 0; i < v.NumField(); i++ { + // Skip unexported fields. + vtf := vt.Field(i) + if vtf.PkgPath != "" { + continue + } + + vf := v.Field(i) + tag := parseTag(vtf.Tag) + + // RFC Section 4.19 - Optional data + if tag.Get("optional") == "true" { + if vf.Type().Kind() != reflect.Ptr { + msg := fmt.Sprintf("optional must be a pointer, not '%v'", + vf.Type().String()) + err := unmarshalError("decodeStruct", ErrBadOptional, + msg, nil, nil) + return n, err + } + + hasopt, n2, err := d.DecodeBool() + n += n2 + if err != nil { + return n, err + } + if !hasopt { + continue + } + } + + // Indirect through pointers allocating them as needed and + // ensure the field is settable. + vf, err := d.indirect(vf) + if err != nil { + return n, err + } + + if !vf.CanSet() { + msg := fmt.Sprintf("can't decode to unsettable '%v'", + vf.Type().String()) + err := unmarshalError("decodeStruct", ErrNotSettable, + msg, nil, nil) + return n, err + } + + // Handle non-opaque data to []uint8 and [#]uint8 based on + // struct tag. + if tag.Get("opaque") == "false" { + switch vf.Kind() { + case reflect.Slice: + n2, err := d.decodeArray(vf, true) + n += n2 + if err != nil { + return n, err + } + continue + + case reflect.Array: + n2, err := d.decodeFixedArray(vf, true) + n += n2 + if err != nil { + return n, err + } + continue + } + } + + if union != "" { + ucase := tag.Get("unioncase") + if ucase != "" && ucase != union { + continue + } + } + + // Decode each struct field. + n2, err := d.decode(vf) + n += n2 + if err != nil { + return n, err + } + + if tag.Get("union") == "true" { + if vf.Type().ConvertibleTo(reflect.TypeOf(0)) { + union = strconv.Itoa(int(vf.Convert(reflect.TypeOf(0)).Int())) + } else if vf.Kind() == reflect.Bool { + if vf.Bool() { + union = "1" + } else { + union = "0" + } + } else { + msg := fmt.Sprintf("type '%s' is not valid", vf.Kind().String()) + return n, unmarshalError("decodeStruct", ErrBadDiscriminant, msg, nil, nil) + } + } + + } + + return n, nil +} + +// RFC Section 4.16 - Void +// RFC Section 4.17 - Constant +// RFC Section 4.18 - Typedef +// RFC Section 4.19 - Optional data +// RFC Sections 4.15 though 4.19 only apply to the data specification language +// which is not implemented by this package. + +// decodeMap treats the next bytes as an XDR encoded variable array of 2-element +// structures whose fields are of the same type as the map keys and elements +// represented by the passed reflection value. Pointers are automatically +// indirected and allocated as necessary. It returns the the number of bytes +// actually read. +// +// An UnmarshalError is returned if any issues are encountered while decoding +// the elements. +func (d *Decoder) decodeMap(v reflect.Value) (int, error) { + dataLen, n, err := d.DecodeUint() + if err != nil { + return n, err + } + + // Allocate storage for the underlying map if needed. + vt := v.Type() + if v.IsNil() { + v.Set(reflect.MakeMap(vt)) + } + + // Decode each key and value according to their type. + keyType := vt.Key() + elemType := vt.Elem() + for i := uint32(0); i < dataLen; i++ { + key := reflect.New(keyType).Elem() + n2, err := d.decode(key) + n += n2 + if err != nil { + return n, err + } + + val := reflect.New(elemType).Elem() + n2, err = d.decode(val) + n += n2 + if err != nil { + return n, err + } + v.SetMapIndex(key, val) + } + return n, nil +} + +// decodeInterface examines the interface represented by the passed reflection +// value to detect whether it is an interface that can be decoded into and +// if it is, extracts the underlying value to pass back into the decode function +// for decoding according to its type. It returns the the number of bytes +// actually read. +// +// An UnmarshalError is returned if any issues are encountered while decoding +// the interface. +func (d *Decoder) decodeInterface(v reflect.Value) (int, error) { + if v.IsNil() || !v.CanInterface() { + msg := fmt.Sprintf("can't decode to nil interface") + err := unmarshalError("decodeInterface", ErrNilInterface, msg, + nil, nil) + return 0, err + } + + // Extract underlying value from the interface and indirect through + // pointers allocating them as needed. + ve := reflect.ValueOf(v.Interface()) + ve, err := d.indirect(ve) + if err != nil { + return 0, err + } + if !ve.CanSet() { + msg := fmt.Sprintf("can't decode to unsettable '%v'", + ve.Type().String()) + err := unmarshalError("decodeInterface", ErrNotSettable, msg, + nil, nil) + return 0, err + } + return d.decode(ve) +} + +// decode is the main workhorse for unmarshalling via reflection. It uses +// the passed reflection value to choose the XDR primitives to decode from +// the encapsulated reader. It is a recursive function, +// so cyclic data structures are not supported and will result in an infinite +// loop. It returns the the number of bytes actually read. +func (d *Decoder) decode(v reflect.Value) (int, error) { + if !v.IsValid() { + msg := fmt.Sprintf("type '%s' is not valid", v.Kind().String()) + err := unmarshalError("decode", ErrUnsupportedType, msg, nil, nil) + return 0, err + } + + // Indirect through pointers allocating them as needed. + ve, err := d.indirect(v) + if err != nil { + return 0, err + } + + // Handle time.Time values by decoding them as an RFC3339 formatted + // string with nanosecond precision. Check the type string rather + // than doing a full blown conversion to interface and type assertion + // since checking a string is much quicker. + if ve.Type().String() == "time.Time" { + // Read the value as a string and parse it. + timeString, n, err := d.DecodeString() + if err != nil { + return n, err + } + ttv, err := time.Parse(time.RFC3339, timeString) + if err != nil { + err := unmarshalError("decode", ErrParseTime, + err.Error(), timeString, err) + return n, err + } + ve.Set(reflect.ValueOf(ttv)) + return n, nil + } + + // Handle native Go types. + switch ve.Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int: + i, n, err := d.DecodeInt() + if err != nil { + return n, err + } + if ve.OverflowInt(int64(i)) { + msg := fmt.Sprintf("signed integer too large to fit '%s'", + ve.Kind().String()) + err = unmarshalError("decode", ErrOverflow, msg, i, nil) + return n, err + } + ve.SetInt(int64(i)) + return n, nil + + case reflect.Int64: + i, n, err := d.DecodeHyper() + if err != nil { + return n, err + } + ve.SetInt(i) + return n, nil + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint: + ui, n, err := d.DecodeUint() + if err != nil { + return n, err + } + if ve.OverflowUint(uint64(ui)) { + msg := fmt.Sprintf("unsigned integer too large to fit '%s'", + ve.Kind().String()) + err = unmarshalError("decode", ErrOverflow, msg, ui, nil) + return n, err + } + ve.SetUint(uint64(ui)) + return n, nil + + case reflect.Uint64: + ui, n, err := d.DecodeUhyper() + if err != nil { + return n, err + } + ve.SetUint(ui) + return n, nil + + case reflect.Bool: + b, n, err := d.DecodeBool() + if err != nil { + return n, err + } + ve.SetBool(b) + return n, nil + + case reflect.Float32: + f, n, err := d.DecodeFloat() + if err != nil { + return n, err + } + ve.SetFloat(float64(f)) + return n, nil + + case reflect.Float64: + f, n, err := d.DecodeDouble() + if err != nil { + return n, err + } + ve.SetFloat(f) + return n, nil + + case reflect.String: + s, n, err := d.DecodeString() + if err != nil { + return n, err + } + ve.SetString(s) + return n, nil + + case reflect.Array: + n, err := d.decodeFixedArray(ve, false) + if err != nil { + return n, err + } + return n, nil + + case reflect.Slice: + n, err := d.decodeArray(ve, false) + if err != nil { + return n, err + } + return n, nil + + case reflect.Struct: + n, err := d.decodeStruct(ve) + if err != nil { + return n, err + } + return n, nil + + case reflect.Map: + n, err := d.decodeMap(ve) + if err != nil { + return n, err + } + return n, nil + + case reflect.Interface: + n, err := d.decodeInterface(ve) + if err != nil { + return n, err + } + return n, nil + } + + // The only unhandled types left are unsupported. At the time of this + // writing the only remaining unsupported types that exist are + // reflect.Uintptr and reflect.UnsafePointer. + msg := fmt.Sprintf("unsupported Go type '%s'", ve.Kind().String()) + err = unmarshalError("decode", ErrUnsupportedType, msg, nil, nil) + return 0, err +} + +// indirect dereferences pointers allocating them as needed until it reaches +// a non-pointer. This allows transparent decoding through arbitrary levels +// of indirection. +func (d *Decoder) indirect(v reflect.Value) (reflect.Value, error) { + rv := v + for rv.Kind() == reflect.Ptr { + // Allocate pointer if needed. + isNil := rv.IsNil() + if isNil && !rv.CanSet() { + msg := fmt.Sprintf("unable to allocate pointer for '%v'", + rv.Type().String()) + err := unmarshalError("indirect", ErrNotSettable, msg, + nil, nil) + return rv, err + } + if isNil { + rv.Set(reflect.New(rv.Type().Elem())) + } + rv = rv.Elem() + } + return rv, nil +} + +// Decode operates identically to the Unmarshal function with the exception of +// using the reader associated with the Decoder as the source of XDR-encoded +// data instead of a user-supplied reader. See the Unmarhsal documentation for +// specifics. +func (d *Decoder) Decode(v interface{}) (int, error) { + if v == nil { + msg := "can't unmarshal to nil interface" + return 0, unmarshalError("Unmarshal", ErrNilInterface, msg, nil, + nil) + } + + vv := reflect.ValueOf(v) + if vv.Kind() != reflect.Ptr { + msg := fmt.Sprintf("can't unmarshal to non-pointer '%v' - use "+ + "& operator", vv.Type().String()) + err := unmarshalError("Unmarshal", ErrBadArguments, msg, nil, nil) + return 0, err + } + if vv.IsNil() && !vv.CanSet() { + msg := fmt.Sprintf("can't unmarshal to unsettable '%v' - use "+ + "& operator", vv.Type().String()) + err := unmarshalError("Unmarshal", ErrNotSettable, msg, nil, nil) + return 0, err + } + + return d.decode(vv) +} + +// NewDecoder returns a Decoder that can be used to manually decode XDR data +// from a provided reader. Typically, Unmarshal should be used instead of +// manually creating a Decoder. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{r: r} +} + +// NewDecoderLimited is identical to NewDecoder but it sets maxReadSize in +// order to cap reads. +func NewDecoderLimited(r io.Reader, maxSize uint) *Decoder { + return &Decoder{r: r, maxReadSize: maxSize} +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/doc.go b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/doc.go new file mode 100644 index 00000000..1904d2b8 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/doc.go @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2012-2014 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +Package xdr implements the data representation portion of the External Data +Representation (XDR) standard protocol as specified in RFC 4506 (obsoletes +RFC 1832 and RFC 1014). + +The XDR RFC defines both a data specification language and a data +representation standard. This package implements methods to encode and decode +XDR data per the data representation standard with the exception of 128-bit +quadruple-precision floating points. It does not currently implement parsing of +the data specification language. In other words, the ability to automatically +generate Go code by parsing an XDR data specification file (typically .x +extension) is not supported. In practice, this limitation of the package is +fairly minor since it is largely unnecessary due to the reflection capabilities +of Go as described below. + +This package provides two approaches for encoding and decoding XDR data: + + 1) Marshal/Unmarshal functions which automatically map between XDR and Go types + 2) Individual Encoder/Decoder objects to manually work with XDR primitives + +For the Marshal/Unmarshal functions, Go reflection capabilities are used to +choose the type of the underlying XDR data based upon the Go type to encode or +the target Go type to decode into. A description of how each type is mapped is +provided below, however one important type worth reviewing is Go structs. In +the case of structs, each exported field (first letter capitalized) is reflected +and mapped in order. As a result, this means a Go struct with exported fields +of the appropriate types listed in the expected order can be used to +automatically encode / decode the XDR data thereby eliminating the need to write +a lot of boilerplate code to encode/decode and error check each piece of XDR +data as is typically required with C based XDR libraries. + +Go Type to XDR Type Mappings + +The following chart shows an overview of how Go types are mapped to XDR types +for automatic marshalling and unmarshalling. The documentation for the Marshal +and Unmarshal functions has specific details of how the mapping proceeds. + + Go Type <-> XDR Type + -------------------- + int8, int16, int32, int <-> XDR Integer + uint8, uint16, uint32, uint <-> XDR Unsigned Integer + int64 <-> XDR Hyper Integer + uint64 <-> XDR Unsigned Hyper Integer + bool <-> XDR Boolean + float32 <-> XDR Floating-Point + float64 <-> XDR Double-Precision Floating-Point + string <-> XDR String + byte <-> XDR Integer + []byte <-> XDR Variable-Length Opaque Data + [#]byte <-> XDR Fixed-Length Opaque Data + [] <-> XDR Variable-Length Array + [#] <-> XDR Fixed-Length Array + * <-> XDR Optional data (when marked with struct tag `xdr:"optional"`) + struct <-> XDR Structure or Discriminated Unions + map <-> XDR Variable-Length Array of two-element XDR Structures + time.Time <-> XDR String encoded with RFC3339 nanosecond precision + +Notes and Limitations: + + * Automatic marshalling and unmarshalling of variable and fixed-length + arrays of uint8s require a special struct tag `xdr:"opaque=false"` + since byte slices and byte arrays are assumed to be opaque data and + byte is a Go alias for uint8 thus indistinguishable under reflection + * Channel, complex, and function types cannot be encoded + * Interfaces without a concrete value cannot be encoded + * Cyclic data structures are not supported and will result in infinite + loops + * Strings are marshalled and unmarshalled with UTF-8 character encoding + which differs from the XDR specification of ASCII, however UTF-8 is + backwards compatible with ASCII so this should rarely cause issues + + +Encoding + +To encode XDR data, use the Marshal function. + func Marshal(w io.Writer, v interface{}) (int, error) + +For example, given the following code snippet: + + type ImageHeader struct { + Signature [3]byte + Version uint32 + IsGrayscale bool + NumSections uint32 + } + h := ImageHeader{[3]byte{0xAB, 0xCD, 0xEF}, 2, true, 10} + + var w bytes.Buffer + bytesWritten, err := xdr.Marshal(&w, &h) + // Error check elided + +The result, encodedData, will then contain the following XDR encoded byte +sequence: + + 0xAB, 0xCD, 0xEF, 0x00, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x0A + + +In addition, while the automatic marshalling discussed above will work for the +vast majority of cases, an Encoder object is provided that can be used to +manually encode XDR primitives for complex scenarios where automatic +reflection-based encoding won't work. The included examples provide a sample of +manual usage via an Encoder. + + +Decoding + +To decode XDR data, use the Unmarshal function. + func Unmarshal(r io.Reader, v interface{}) (int, error) + +For example, given the following code snippet: + + type ImageHeader struct { + Signature [3]byte + Version uint32 + IsGrayscale bool + NumSections uint32 + } + + // Using output from the Encoding section above. + encodedData := []byte{ + 0xAB, 0xCD, 0xEF, 0x00, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x0A, + } + + var h ImageHeader + bytesRead, err := xdr.Unmarshal(bytes.NewReader(encodedData), &h) + // Error check elided + +The struct instance, h, will then contain the following values: + + h.Signature = [3]byte{0xAB, 0xCD, 0xEF} + h.Version = 2 + h.IsGrayscale = true + h.NumSections = 10 + +In addition, while the automatic unmarshalling discussed above will work for the +vast majority of cases, a Decoder object is provided that can be used to +manually decode XDR primitives for complex scenarios where automatic +reflection-based decoding won't work. The included examples provide a sample of +manual usage via a Decoder. + + +Discriminated Unions + +Discriminated unions are marshalled via Go structs, using special struct tags +to mark the discriminant and the different cases. For instance: + + type ReturnValue struct { + Status int `xdr:"union"` + StatusOk struct { + Width int + Height int + } `xdr:"unioncase=0"` + StatusError struct { + ErrMsg string + } `xdr:"unioncase=-1"` + } + +The Status field is the discriminant of the union, and is always serialized; +if its value is 0, the StatusOK struct is serialized while the StatusErr struct +is ignored; if its value is -1, the opposite happens. If the value is different +from both 0 and -1, only the Status field is serialized. Any additional field +not marked with unioncase is always serialized as normal. + +You are not forced to use sub-structures; for instance, the following is also +valid: + + type ReturnValue struct { + Status int `xdr:"union"` + Width int `xdr:"unioncase=0"` + Height int `xdr:"unioncase=0"` + ErrMsg string `xdr:"unioncase=-1"` + } + + +Errors + +All errors are either of type UnmarshalError or MarshalError. Both provide +human-readable output as well as an ErrorCode field which can be inspected by +sophisticated callers if necessary. + +See the documentation of UnmarshalError, MarshalError, and ErrorCode for further +details. +*/ +package xdr diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/encode.go b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/encode.go new file mode 100644 index 00000000..73a2ff00 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/encode.go @@ -0,0 +1,718 @@ +/* + * Copyright (c) 2012-2014 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package xdr + +import ( + "fmt" + "io" + "math" + "reflect" + "strconv" + "time" +) + +var errIOEncode = "%s while encoding %d bytes" + +/* +Marshal writes the XDR encoding of v to writer w and returns the number of bytes +written. It traverses v recursively and automatically indirects pointers +through arbitrary depth to encode the actual value pointed to. + +Marshal uses reflection to determine the type of the concrete value contained by +v and performs a mapping of Go types to the underlying XDR types as follows: + + Go Type -> XDR Type + -------------------- + int8, int16, int32, int -> XDR Integer + uint8, uint16, uint32, uint -> XDR Unsigned Integer + int64 -> XDR Hyper Integer + uint64 -> XDR Unsigned Hyper Integer + bool -> XDR Boolean + float32 -> XDR Floating-Point + float64 -> XDR Double-Precision Floating-Point + string -> XDR String + byte -> XDR Integer + []byte -> XDR Variable-Length Opaque Data + [#]byte -> XDR Fixed-Length Opaque Data + [] -> XDR Variable-Length Array + [#] -> XDR Fixed-Length Array + struct -> XDR Structure + map -> XDR Variable-Length Array of two-element XDR Structures + time.Time -> XDR String encoded with RFC3339 nanosecond precision + +Notes and Limitations: + + * Automatic marshalling of variable and fixed-length arrays of uint8s + requires a special struct tag `xdropaque:"false"` since byte slices and + byte arrays are assumed to be opaque data and byte is a Go alias for uint8 + thus indistinguishable under reflection + * Channel, complex, and function types cannot be encoded + * Interfaces without a concrete value cannot be encoded + * Cyclic data structures are not supported and will result in infinite loops + * Strings are marshalled with UTF-8 character encoding which differs from + the XDR specification of ASCII, however UTF-8 is backwards compatible with + ASCII so this should rarely cause issues + +If any issues are encountered during the marshalling process, a MarshalError is +returned with a human readable description as well as an ErrorCode value for +further inspection from sophisticated callers. Some potential issues are +unsupported Go types, attempting to encode more opaque data than can be +represented by a single opaque XDR entry, and exceeding max slice limitations. +*/ +func Marshal(w io.Writer, v interface{}) (int, error) { + enc := Encoder{w: w} + return enc.Encode(v) +} + +// An Encoder wraps an io.Writer that will receive the XDR encoded byte stream. +// See NewEncoder. +type Encoder struct { + w io.Writer +} + +// EncodeInt writes the XDR encoded representation of the passed 32-bit signed +// integer to the encapsulated writer and returns the number of bytes written. +// +// A MarshalError with an error code of ErrIO is returned if writing the data +// fails. +// +// Reference: +// RFC Section 4.1 - Integer +// 32-bit big-endian signed integer in range [-2147483648, 2147483647] +func (enc *Encoder) EncodeInt(v int32) (int, error) { + var b [4]byte + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) + + n, err := enc.w.Write(b[:]) + if err != nil { + msg := fmt.Sprintf(errIOEncode, err.Error(), 4) + err := marshalError("EncodeInt", ErrIO, msg, b[:n], err) + return n, err + } + + return n, nil +} + +// EncodeUint writes the XDR encoded representation of the passed 32-bit +// unsigned integer to the encapsulated writer and returns the number of bytes +// written. +// +// A MarshalError with an error code of ErrIO is returned if writing the data +// fails. +// +// Reference: +// RFC Section 4.2 - Unsigned Integer +// 32-bit big-endian unsigned integer in range [0, 4294967295] +func (enc *Encoder) EncodeUint(v uint32) (int, error) { + var b [4]byte + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) + + n, err := enc.w.Write(b[:]) + if err != nil { + msg := fmt.Sprintf(errIOEncode, err.Error(), 4) + err := marshalError("EncodeUint", ErrIO, msg, b[:n], err) + return n, err + } + + return n, nil +} + +// EncodeEnum treats the passed 32-bit signed integer as an enumeration value +// and, if it is in the list of passed valid enumeration values, writes the XDR +// encoded representation of it to the encapsulated writer. It returns the +// number of bytes written. +// +// A MarshalError is returned if the enumeration value is not one of the +// provided valid values or if writing the data fails. +// +// Reference: +// RFC Section 4.3 - Enumeration +// Represented as an XDR encoded signed integer +func (enc *Encoder) EncodeEnum(v int32, validEnums map[int32]bool) (int, error) { + if !validEnums[v] { + err := marshalError("EncodeEnum", ErrBadEnumValue, + "invalid enum", v, nil) + return 0, err + } + return enc.EncodeInt(v) +} + +// EncodeBool writes the XDR encoded representation of the passed boolean to the +// encapsulated writer and returns the number of bytes written. +// +// A MarshalError with an error code of ErrIO is returned if writing the data +// fails. +// +// Reference: +// RFC Section 4.4 - Boolean +// Represented as an XDR encoded enumeration where 0 is false and 1 is true +func (enc *Encoder) EncodeBool(v bool) (int, error) { + i := int32(0) + if v == true { + i = 1 + } + return enc.EncodeInt(i) +} + +// EncodeHyper writes the XDR encoded representation of the passed 64-bit +// signed integer to the encapsulated writer and returns the number of bytes +// written. +// +// A MarshalError with an error code of ErrIO is returned if writing the data +// fails. +// +// Reference: +// RFC Section 4.5 - Hyper Integer +// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] +func (enc *Encoder) EncodeHyper(v int64) (int, error) { + var b [8]byte + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) + + n, err := enc.w.Write(b[:]) + if err != nil { + msg := fmt.Sprintf(errIOEncode, err.Error(), 8) + err := marshalError("EncodeHyper", ErrIO, msg, b[:n], err) + return n, err + } + + return n, nil +} + +// EncodeUhyper writes the XDR encoded representation of the passed 64-bit +// unsigned integer to the encapsulated writer and returns the number of bytes +// written. +// +// A MarshalError with an error code of ErrIO is returned if writing the data +// fails. +// +// Reference: +// RFC Section 4.5 - Unsigned Hyper Integer +// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] +func (enc *Encoder) EncodeUhyper(v uint64) (int, error) { + var b [8]byte + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) + + n, err := enc.w.Write(b[:]) + if err != nil { + msg := fmt.Sprintf(errIOEncode, err.Error(), 8) + err := marshalError("EncodeUhyper", ErrIO, msg, b[:n], err) + return n, err + } + + return n, nil +} + +// EncodeFloat writes the XDR encoded representation of the passed 32-bit +// (single-precision) floating point to the encapsulated writer and returns the +// number of bytes written. +// +// A MarshalError with an error code of ErrIO is returned if writing the data +// fails. +// +// Reference: +// RFC Section 4.6 - Floating Point +// 32-bit single-precision IEEE 754 floating point +func (enc *Encoder) EncodeFloat(v float32) (int, error) { + ui := math.Float32bits(v) + return enc.EncodeUint(ui) +} + +// EncodeDouble writes the XDR encoded representation of the passed 64-bit +// (double-precision) floating point to the encapsulated writer and returns the +// number of bytes written. +// +// A MarshalError with an error code of ErrIO is returned if writing the data +// fails. +// +// Reference: +// RFC Section 4.7 - Double-Precision Floating Point +// 64-bit double-precision IEEE 754 floating point +func (enc *Encoder) EncodeDouble(v float64) (int, error) { + ui := math.Float64bits(v) + return enc.EncodeUhyper(ui) +} + +// RFC Section 4.8 - Quadruple-Precision Floating Point +// 128-bit quadruple-precision floating point +// Not Implemented + +// EncodeFixedOpaque treats the passed byte slice as opaque data of a fixed +// size and writes the XDR encoded representation of it to the encapsulated +// writer. It returns the number of bytes written. +// +// A MarshalError with an error code of ErrIO is returned if writing the data +// fails. +// +// Reference: +// RFC Section 4.9 - Fixed-Length Opaque Data +// Fixed-length uninterpreted data zero-padded to a multiple of four +func (enc *Encoder) EncodeFixedOpaque(v []byte) (int, error) { + l := len(v) + pad := (4 - (l % 4)) % 4 + + // Write the actual bytes. + n, err := enc.w.Write(v) + if err != nil { + msg := fmt.Sprintf(errIOEncode, err.Error(), len(v)) + err := marshalError("EncodeFixedOpaque", ErrIO, msg, v[:n], err) + return n, err + } + + // Write any padding if needed. + if pad > 0 { + b := make([]byte, pad) + n2, err := enc.w.Write(b) + n += n2 + if err != nil { + written := make([]byte, l+n2) + copy(written, v) + copy(written[l:], b[:n2]) + msg := fmt.Sprintf(errIOEncode, err.Error(), l+pad) + err := marshalError("EncodeFixedOpaque", ErrIO, msg, + written, err) + return n, err + } + } + + return n, nil +} + +// EncodeOpaque treats the passed byte slice as opaque data of a variable +// size and writes the XDR encoded representation of it to the encapsulated +// writer. It returns the number of bytes written. +// +// A MarshalError with an error code of ErrIO is returned if writing the data +// fails. +// +// Reference: +// RFC Section 4.10 - Variable-Length Opaque Data +// Unsigned integer length followed by fixed opaque data of that length +func (enc *Encoder) EncodeOpaque(v []byte) (int, error) { + // Length of opaque data. + n, err := enc.EncodeUint(uint32(len(v))) + if err != nil { + return n, err + } + + n2, err := enc.EncodeFixedOpaque(v) + n += n2 + return n, err +} + +// EncodeString writes the XDR encoded representation of the passed string +// to the encapsulated writer and returns the number of bytes written. +// Character encoding is assumed to be UTF-8 and therefore ASCII compatible. If +// the underlying character encoding is not compatible with this assumption, the +// data can instead be written as variable-length opaque data (EncodeOpaque) and +// manually converted as needed. +// +// A MarshalError with an error code of ErrIO is returned if writing the data +// fails. +// +// Reference: +// RFC Section 4.11 - String +// Unsigned integer length followed by bytes zero-padded to a multiple of four +func (enc *Encoder) EncodeString(v string) (int, error) { + // Length of string. + n, err := enc.EncodeUint(uint32(len(v))) + if err != nil { + return n, err + } + + n2, err := enc.EncodeFixedOpaque([]byte(v)) + n += n2 + return n, err +} + +// encodeFixedArray writes the XDR encoded representation of each element +// in the passed array represented by the reflection value to the encapsulated +// writer and returns the number of bytes written. The ignoreOpaque flag +// controls whether or not uint8 (byte) elements should be encoded individually +// or as a fixed sequence of opaque data. +// +// A MarshalError is returned if any issues are encountered while encoding +// the array elements. +// +// Reference: +// RFC Section 4.12 - Fixed-Length Array +// Individually XDR encoded array elements +func (enc *Encoder) encodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, error) { + // Treat [#]byte (byte is alias for uint8) as opaque data unless ignored. + if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 { + // Create a slice of the underlying array for better efficiency + // when possible. Can't create a slice of an unaddressable + // value. + if v.CanAddr() { + return enc.EncodeFixedOpaque(v.Slice(0, v.Len()).Bytes()) + } + + // When the underlying array isn't addressable fall back to + // copying the array into a new slice. This is rather ugly, but + // the inability to create a constant slice from an + // unaddressable array is a limitation of Go. + slice := make([]byte, v.Len(), v.Len()) + reflect.Copy(reflect.ValueOf(slice), v) + return enc.EncodeFixedOpaque(slice) + } + + // Encode each array element. + var n int + for i := 0; i < v.Len(); i++ { + n2, err := enc.encode(v.Index(i)) + n += n2 + if err != nil { + return n, err + } + } + + return n, nil +} + +// encodeArray writes an XDR encoded integer representing the number of +// elements in the passed slice represented by the reflection value followed by +// the XDR encoded representation of each element in slice to the encapsulated +// writer and returns the number of bytes written. The ignoreOpaque flag +// controls whether or not uint8 (byte) elements should be encoded individually +// or as a variable sequence of opaque data. +// +// A MarshalError is returned if any issues are encountered while encoding +// the array elements. +// +// Reference: +// RFC Section 4.13 - Variable-Length Array +// Unsigned integer length followed by individually XDR encoded array elements +func (enc *Encoder) encodeArray(v reflect.Value, ignoreOpaque bool) (int, error) { + numItems := uint32(v.Len()) + n, err := enc.EncodeUint(numItems) + if err != nil { + return n, err + } + + n2, err := enc.encodeFixedArray(v, ignoreOpaque) + n += n2 + return n, err +} + +// encodeStruct writes an XDR encoded representation of each value in the +// exported fields of the struct represented by the passed reflection value to +// the encapsulated writer and returns the number of bytes written. Pointers +// are automatically indirected through arbitrary depth to encode the actual +// value pointed to. +// +// A MarshalError is returned if any issues are encountered while encoding +// the elements. +// +// Reference: +// RFC Section 4.14 - Structure +// XDR encoded elements in the order of their declaration in the struct +func (enc *Encoder) encodeStruct(v reflect.Value) (int, error) { + var n int + var union string + vt := v.Type() + for i := 0; i < v.NumField(); i++ { + // Skip unexported fields and indirect through pointers. + vtf := vt.Field(i) + if vtf.PkgPath != "" { + continue + } + + vf := v.Field(i) + tag := parseTag(vtf.Tag) + + // RFC Section 4.19 - Optional data + if tag.Get("optional") == "true" { + if vf.Type().Kind() != reflect.Ptr { + msg := fmt.Sprintf("optional must be a pointer, not '%v'", + vf.Type().String()) + err := marshalError("encodeStruct", ErrBadOptional, + msg, nil, nil) + return n, err + } + + hasopt := !vf.IsNil() + n2, err := enc.EncodeBool(hasopt) + n += n2 + if err != nil { + return n, err + } + if !hasopt { + continue + } + } + + vf = enc.indirect(vf) + + // Handle non-opaque data to []uint8 and [#]uint8 based on struct tag. + if tag.Get("opaque") == "false" { + switch vf.Kind() { + case reflect.Slice: + n2, err := enc.encodeArray(vf, true) + n += n2 + if err != nil { + return n, err + } + continue + + case reflect.Array: + n2, err := enc.encodeFixedArray(vf, true) + n += n2 + if err != nil { + return n, err + } + continue + } + } + + // RFC Section 4.15 - Discriminated Union + // The tag option "union" marks the discriminant in the struct; the tag + // option "unioncase=N" marks a struct field that is only serialized + // when the discriminant has the specified value. + if tag.Get("union") == "true" { + if vf.Type().ConvertibleTo(reflect.TypeOf(0)) { + union = strconv.Itoa(int(vf.Convert(reflect.TypeOf(0)).Int())) + } else if vf.Kind() == reflect.Bool { + if vf.Bool() { + union = "1" + } else { + union = "0" + } + } else { + msg := fmt.Sprintf("type '%s' is not valid", vf.Kind().String()) + return n, marshalError("encodeStruct", ErrBadDiscriminant, msg, nil, nil) + } + } + + if union != "" { + ucase := tag.Get("unioncase") + if ucase != "" && ucase != union { + continue + } + } + + // Encode each struct field. + n2, err := enc.encode(vf) + n += n2 + if err != nil { + return n, err + } + } + + return n, nil +} + +// RFC Section 4.16 - Void +// RFC Section 4.17 - Constant +// RFC Section 4.18 - Typedef +// RFC Section 4.19 - Optional data +// RFC Sections 4.16 though 4.19 only apply to the data specification language +// which is not implemented by this package. + +// encodeMap treats the map represented by the passed reflection value as a +// variable-length array of 2-element structures whose fields are of the same +// type as the map keys and elements and writes its XDR encoded representation +// to the encapsulated writer. It returns the number of bytes written. +// +// A MarshalError is returned if any issues are encountered while encoding +// the elements. +func (enc *Encoder) encodeMap(v reflect.Value) (int, error) { + // Number of elements. + n, err := enc.EncodeUint(uint32(v.Len())) + if err != nil { + return n, err + } + + // Encode each key and value according to their type. + for _, key := range v.MapKeys() { + n2, err := enc.encode(key) + n += n2 + if err != nil { + return n, err + } + + n2, err = enc.encode(v.MapIndex(key)) + n += n2 + if err != nil { + return n, err + } + } + + return n, nil +} + +// encodeInterface examines the interface represented by the passed reflection +// value to detect whether it is an interface that can be encoded if it is, +// extracts the underlying value to pass back into the encode function for +// encoding according to its type. +// +// A MarshalError is returned if any issues are encountered while encoding +// the interface. +func (enc *Encoder) encodeInterface(v reflect.Value) (int, error) { + if v.IsNil() || !v.CanInterface() { + msg := fmt.Sprintf("can't encode nil interface") + err := marshalError("encodeInterface", ErrNilInterface, msg, + nil, nil) + return 0, err + } + + // Extract underlying value from the interface and indirect through pointers. + ve := reflect.ValueOf(v.Interface()) + ve = enc.indirect(ve) + return enc.encode(ve) +} + +// encode is the main workhorse for marshalling via reflection. It uses +// the passed reflection value to choose the XDR primitives to encode into +// the encapsulated writer and returns the number of bytes written. It is a +// recursive function, so cyclic data structures are not supported and will +// result in an infinite loop. +func (enc *Encoder) encode(v reflect.Value) (int, error) { + if !v.IsValid() { + msg := fmt.Sprintf("type '%s' is not valid", v.Kind().String()) + err := marshalError("encode", ErrUnsupportedType, msg, nil, nil) + return 0, err + } + + // Indirect through pointers to get at the concrete value. + ve := enc.indirect(v) + + // Handle time.Time values by encoding them as an RFC3339 formatted + // string with nanosecond precision. Check the type string before + // doing a full blown conversion to interface and type assertion since + // checking a string is much quicker. + if ve.Type().String() == "time.Time" && ve.CanInterface() { + viface := ve.Interface() + if tv, ok := viface.(time.Time); ok { + return enc.EncodeString(tv.Format(time.RFC3339Nano)) + } + } + + // Handle native Go types. + switch ve.Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int: + return enc.EncodeInt(int32(ve.Int())) + + case reflect.Int64: + return enc.EncodeHyper(ve.Int()) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint: + return enc.EncodeUint(uint32(ve.Uint())) + + case reflect.Uint64: + return enc.EncodeUhyper(ve.Uint()) + + case reflect.Bool: + return enc.EncodeBool(ve.Bool()) + + case reflect.Float32: + return enc.EncodeFloat(float32(ve.Float())) + + case reflect.Float64: + return enc.EncodeDouble(ve.Float()) + + case reflect.String: + return enc.EncodeString(ve.String()) + + case reflect.Array: + return enc.encodeFixedArray(ve, false) + + case reflect.Slice: + return enc.encodeArray(ve, false) + + case reflect.Struct: + return enc.encodeStruct(ve) + + case reflect.Map: + return enc.encodeMap(ve) + + case reflect.Interface: + return enc.encodeInterface(ve) + } + + // The only unhandled types left are unsupported. At the time of this + // writing the only remaining unsupported types that exist are + // reflect.Uintptr and reflect.UnsafePointer. + msg := fmt.Sprintf("unsupported Go type '%s'", ve.Kind().String()) + err := marshalError("encode", ErrUnsupportedType, msg, nil, nil) + return 0, err +} + +// indirect dereferences pointers until it reaches a non-pointer. This allows +// transparent encoding through arbitrary levels of indirection. +func (enc *Encoder) indirect(v reflect.Value) reflect.Value { + rv := v + for rv.Kind() == reflect.Ptr { + rv = rv.Elem() + } + return rv +} + +// Encode operates identically to the Marshal function with the exception of +// using the writer associated with the Encoder for the destination of the +// XDR-encoded data instead of a user-supplied writer. See the Marshal +// documentation for specifics. +func (enc *Encoder) Encode(v interface{}) (int, error) { + if v == nil { + msg := "can't marshal nil interface" + err := marshalError("Marshal", ErrNilInterface, msg, nil, nil) + return 0, err + } + + vv := reflect.ValueOf(v) + vve := vv + for vve.Kind() == reflect.Ptr { + if vve.IsNil() { + msg := fmt.Sprintf("can't marshal nil pointer '%v'", + vv.Type().String()) + err := marshalError("Marshal", ErrBadArguments, msg, + nil, nil) + return 0, err + } + vve = vve.Elem() + } + + return enc.encode(vve) +} + +// NewEncoder returns an object that can be used to manually choose fields to +// XDR encode to the passed writer w. Typically, Marshal should be used instead +// of manually creating an Encoder. An Encoder, along with several of its +// methods to encode XDR primitives, is exposed so it is possible to perform +// manual encoding of data without relying on reflection should it be necessary +// in complex scenarios where automatic reflection-based encoding won't work. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{w: w} +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/error.go b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/error.go new file mode 100644 index 00000000..8c1f3aec --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/error.go @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2012-2014 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package xdr + +import "fmt" + +// ErrorCode identifies a kind of error. +type ErrorCode int + +const ( + // ErrBadArguments indicates arguments passed to the function are not + // what was expected. + ErrBadArguments ErrorCode = iota + + // ErrUnsupportedType indicates the Go type is not a supported type for + // marshalling and unmarshalling XDR data. + ErrUnsupportedType + + // ErrBadEnumValue indicates an enumeration value is not in the list of + // valid values. + ErrBadEnumValue + + // ErrNotSettable indicates an interface value cannot be written to. + // This usually means the interface value was not passed with the & + // operator, but it can also happen if automatic pointer allocation + // fails. + ErrNotSettable + + // ErrOverflow indicates that the data in question is too large to fit + // into the corresponding Go or XDR data type. For example, an integer + // decoded from XDR that is too large to fit into a target type of int8, + // or opaque data that exceeds the max length of a Go slice. + ErrOverflow + + // ErrNilInterface indicates an interface with no concrete type + // information was encountered. Type information is necessary to + // perform mapping between XDR and Go types. + ErrNilInterface + + // ErrIO indicates an error was encountered while reading or writing to + // an io.Reader or io.Writer, respectively. The actual underlying error + // will be available via the Err field of the MarshalError or + // UnmarshalError struct. + ErrIO + + // ErrParseTime indicates an error was encountered while parsing an + // RFC3339 formatted time value. The actual underlying error will be + // available via the Err field of the UnmarshalError struct. + ErrParseTime + + // ErrBadDiscriminant indicates that a non-integer field of a struct + // was marked as a union discriminant through a struct tag. + ErrBadDiscriminant + + // ErrBadOptional indicates that a non-pointer field of a struct + // was marked as an optional-data. + ErrBadOptional +) + +// Map of ErrorCode values back to their constant names for pretty printing. +var errorCodeStrings = map[ErrorCode]string{ + ErrBadArguments: "ErrBadArguments", + ErrUnsupportedType: "ErrUnsupportedType", + ErrBadEnumValue: "ErrBadEnumValue", + ErrNotSettable: "ErrNotSettable", + ErrOverflow: "ErrOverflow", + ErrNilInterface: "ErrNilInterface", + ErrIO: "ErrIO", + ErrParseTime: "ErrParseTime", + ErrBadDiscriminant: "ErrBadDiscriminant", + ErrBadOptional: "ErrBadOptional", +} + +// String returns the ErrorCode as a human-readable name. +func (e ErrorCode) String() string { + if s := errorCodeStrings[e]; s != "" { + return s + } + return fmt.Sprintf("Unknown ErrorCode (%d)", e) +} + +// UnmarshalError describes a problem encountered while unmarshaling data. +// Some potential issues are unsupported Go types, attempting to decode a value +// which is too large to fit into a specified Go type, and exceeding max slice +// limitations. +type UnmarshalError struct { + ErrorCode ErrorCode // Describes the kind of error + Func string // Function name + Value interface{} // Value actually parsed where appropriate + Description string // Human readable description of the issue + Err error // The underlying error for IO errors +} + +// Error satisfies the error interface and prints human-readable errors. +func (e *UnmarshalError) Error() string { + switch e.ErrorCode { + case ErrBadEnumValue, ErrOverflow, ErrIO, ErrParseTime: + return fmt.Sprintf("xdr:%s: %s - read: '%v'", e.Func, + e.Description, e.Value) + } + return fmt.Sprintf("xdr:%s: %s", e.Func, e.Description) +} + +// unmarshalError creates an error given a set of arguments and will copy byte +// slices into the Value field since they might otherwise be changed from from +// the original value. +func unmarshalError(f string, c ErrorCode, desc string, v interface{}, err error) *UnmarshalError { + e := &UnmarshalError{ErrorCode: c, Func: f, Description: desc, Err: err} + switch t := v.(type) { + case []byte: + slice := make([]byte, len(t)) + copy(slice, t) + e.Value = slice + default: + e.Value = v + } + + return e +} + +// IsIO returns a boolean indicating whether the error is known to report that +// the underlying reader or writer encountered an ErrIO. +func IsIO(err error) bool { + switch e := err.(type) { + case *UnmarshalError: + return e.ErrorCode == ErrIO + case *MarshalError: + return e.ErrorCode == ErrIO + } + return false +} + +// MarshalError describes a problem encountered while marshaling data. +// Some potential issues are unsupported Go types, attempting to encode more +// opaque data than can be represented by a single opaque XDR entry, and +// exceeding max slice limitations. +type MarshalError struct { + ErrorCode ErrorCode // Describes the kind of error + Func string // Function name + Value interface{} // Value actually parsed where appropriate + Description string // Human readable description of the issue + Err error // The underlying error for IO errors +} + +// Error satisfies the error interface and prints human-readable errors. +func (e *MarshalError) Error() string { + switch e.ErrorCode { + case ErrIO: + return fmt.Sprintf("xdr:%s: %s - wrote: '%v'", e.Func, + e.Description, e.Value) + case ErrBadEnumValue: + return fmt.Sprintf("xdr:%s: %s - value: '%v'", e.Func, + e.Description, e.Value) + } + return fmt.Sprintf("xdr:%s: %s", e.Func, e.Description) +} + +// marshalError creates an error given a set of arguments and will copy byte +// slices into the Value field since they might otherwise be changed from from +// the original value. +func marshalError(f string, c ErrorCode, desc string, v interface{}, err error) *MarshalError { + e := &MarshalError{ErrorCode: c, Func: f, Description: desc, Err: err} + switch t := v.(type) { + case []byte: + slice := make([]byte, len(t)) + copy(slice, t) + e.Value = slice + default: + e.Value = v + } + + return e +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/tag.go b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/tag.go new file mode 100644 index 00000000..7b5fe24b --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/davecgh/go-xdr/xdr2/tag.go @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015-2017 Giovanni Bajo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package xdr + +import ( + "reflect" + "strings" +) + +// xdrtag represents a XDR struct tag, identified by the name "xdr:". +// The value of the tag is a string that is parsed as a comma-separated +// list of =-separated key-value options. If an option has no value, +// "true" is assumed to be the default value. +// +// For instance: +// +// `xdr:"foo,bar=2,baz=false" +// +// After parsing this tag, Get("foo") will return "true", Get("bar") +// will return "2", and Get("baz") will return "false". +type xdrtag string + +// parseTag extracts a xdrtag from the original reflect.StructTag as found in +// in the struct field. If the tag was not specified, an empty strtag is +// returned. +func parseTag(tag reflect.StructTag) xdrtag { + t := tag.Get("xdr") + // Handle backward compatibility with the previous "xdropaque" + // tag which is now deprecated. + if tag.Get("xdropaque") == "false" { + if t == "" { + t = "," + } + t += ",opaque=false" + } + return xdrtag(t) +} + +// Get returns the value for the specified option. If the option is not +// present in the tag, an empty string is returned. If the option is +// present but has no value, the string "true" is returned as default value. +func (t xdrtag) Get(opt string) string { + tag := string(t) + for tag != "" { + var next string + i := strings.Index(tag, ",") + if i >= 0 { + tag, next = tag[:i], tag[i+1:] + } + if tag == opt { + return "true" + } + if len(tag) > len(opt) && tag[:len(opt)] == opt && tag[len(opt)] == '=' { + return tag[len(opt)+1:] + } + tag = next + } + return "" +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/dce.go b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/dce.go new file mode 100644 index 00000000..a6479dba --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/dce.go @@ -0,0 +1,80 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "fmt" + "os" +) + +// A Domain represents a Version 2 domain +type Domain byte + +// Domain constants for DCE Security (Version 2) UUIDs. +const ( + Person = Domain(0) + Group = Domain(1) + Org = Domain(2) +) + +// NewDCESecurity returns a DCE Security (Version 2) UUID. +// +// The domain should be one of Person, Group or Org. +// On a POSIX system the id should be the users UID for the Person +// domain and the users GID for the Group. The meaning of id for +// the domain Org or on non-POSIX systems is site defined. +// +// For a given domain/id pair the same token may be returned for up to +// 7 minutes and 10 seconds. +func NewDCESecurity(domain Domain, id uint32) (UUID, error) { + uuid, err := NewUUID() + if err == nil { + uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 + uuid[9] = byte(domain) + binary.BigEndian.PutUint32(uuid[0:], id) + } + return uuid, err +} + +// NewDCEPerson returns a DCE Security (Version 2) UUID in the person +// domain with the id returned by os.Getuid. +// +// NewDCEPerson(Person, uint32(os.Getuid())) +func NewDCEPerson() (UUID, error) { + return NewDCESecurity(Person, uint32(os.Getuid())) +} + +// NewDCEGroup returns a DCE Security (Version 2) UUID in the group +// domain with the id returned by os.Getgid. +// +// NewDCEGroup(Group, uint32(os.Getgid())) +func NewDCEGroup() (UUID, error) { + return NewDCESecurity(Group, uint32(os.Getgid())) +} + +// Domain returns the domain for a Version 2 UUID. Domains are only defined +// for Version 2 UUIDs. +func (uuid UUID) Domain() Domain { + return Domain(uuid[9]) +} + +// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2 +// UUIDs. +func (uuid UUID) ID() uint32 { + return binary.BigEndian.Uint32(uuid[0:4]) +} + +func (d Domain) String() string { + switch d { + case Person: + return "Person" + case Group: + return "Group" + case Org: + return "Org" + } + return fmt.Sprintf("Domain%d", int(d)) +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/doc.go b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/doc.go new file mode 100644 index 00000000..5b8a4b9a --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/doc.go @@ -0,0 +1,12 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package uuid generates and inspects UUIDs. +// +// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security +// Services. +// +// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to +// maps or compared directly. +package uuid diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/hash.go b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/hash.go new file mode 100644 index 00000000..4fc5a77d --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/hash.go @@ -0,0 +1,53 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "crypto/md5" + "crypto/sha1" + "hash" +) + +// Well known namespace IDs and UUIDs +var ( + NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) + Nil UUID // empty UUID, all zeros +) + +// NewHash returns a new UUID derived from the hash of space concatenated with +// data generated by h. The hash should be at least 16 byte in length. The +// first 16 bytes of the hash are used to form the UUID. The version of the +// UUID will be the lower 4 bits of version. NewHash is used to implement +// NewMD5 and NewSHA1. +func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { + h.Reset() + h.Write(space[:]) + h.Write([]byte(data)) + s := h.Sum(nil) + var uuid UUID + copy(uuid[:], s) + uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) + uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant + return uuid +} + +// NewMD5 returns a new MD5 (Version 3) UUID based on the +// supplied name space and data. It is the same as calling: +// +// NewHash(md5.New(), space, data, 3) +func NewMD5(space UUID, data []byte) UUID { + return NewHash(md5.New(), space, data, 3) +} + +// NewSHA1 returns a new SHA1 (Version 5) UUID based on the +// supplied name space and data. It is the same as calling: +// +// NewHash(sha1.New(), space, data, 5) +func NewSHA1(space UUID, data []byte) UUID { + return NewHash(sha1.New(), space, data, 5) +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/marshal.go b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/marshal.go new file mode 100644 index 00000000..84bbc588 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/marshal.go @@ -0,0 +1,39 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "fmt" + +// MarshalText implements encoding.TextMarshaler. +func (uuid UUID) MarshalText() ([]byte, error) { + var js [36]byte + encodeHex(js[:], uuid) + return js[:], nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (uuid *UUID) UnmarshalText(data []byte) error { + // See comment in ParseBytes why we do this. + // id, err := ParseBytes(data) + id, err := ParseBytes(data) + if err == nil { + *uuid = id + } + return err +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (uuid UUID) MarshalBinary() ([]byte, error) { + return uuid[:], nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (uuid *UUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(uuid[:], data) + return nil +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/node.go b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/node.go new file mode 100644 index 00000000..5f0156a2 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/node.go @@ -0,0 +1,103 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "net" + "sync" +) + +var ( + nodeMu sync.Mutex + interfaces []net.Interface // cached list of interfaces + ifname string // name of interface being used + nodeID [6]byte // hardware for version 1 UUIDs + zeroID [6]byte // nodeID with only 0's +) + +// NodeInterface returns the name of the interface from which the NodeID was +// derived. The interface "user" is returned if the NodeID was set by +// SetNodeID. +func NodeInterface() string { + defer nodeMu.Unlock() + nodeMu.Lock() + return ifname +} + +// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. +// If name is "" then the first usable interface found will be used or a random +// Node ID will be generated. If a named interface cannot be found then false +// is returned. +// +// SetNodeInterface never fails when name is "". +func SetNodeInterface(name string) bool { + defer nodeMu.Unlock() + nodeMu.Lock() + return setNodeInterface(name) +} + +func setNodeInterface(name string) bool { + if interfaces == nil { + var err error + interfaces, err = net.Interfaces() + if err != nil && name != "" { + return false + } + } + + for _, ifs := range interfaces { + if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { + copy(nodeID[:], ifs.HardwareAddr) + ifname = ifs.Name + return true + } + } + + // We found no interfaces with a valid hardware address. If name + // does not specify a specific interface generate a random Node ID + // (section 4.1.6) + if name == "" { + randomBits(nodeID[:]) + return true + } + return false +} + +// NodeID returns a slice of a copy of the current Node ID, setting the Node ID +// if not already set. +func NodeID() []byte { + defer nodeMu.Unlock() + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + nid := nodeID + return nid[:] +} + +// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes +// of id are used. If id is less than 6 bytes then false is returned and the +// Node ID is not set. +func SetNodeID(id []byte) bool { + if len(id) < 6 { + return false + } + defer nodeMu.Unlock() + nodeMu.Lock() + copy(nodeID[:], id) + ifname = "user" + return true +} + +// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is +// not valid. The NodeID is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) NodeID() []byte { + if len(uuid) != 16 { + return nil + } + var node [6]byte + copy(node[:], uuid[10:]) + return node[:] +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/sql.go b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/sql.go new file mode 100644 index 00000000..f326b54d --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/sql.go @@ -0,0 +1,59 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "database/sql/driver" + "fmt" +) + +// Scan implements sql.Scanner so UUIDs can be read from databases transparently +// Currently, database types that map to string and []byte are supported. Please +// consult database-specific driver documentation for matching types. +func (uuid *UUID) Scan(src interface{}) error { + switch src := src.(type) { + case nil: + return nil + + case string: + // if an empty UUID comes from a table, we return a null UUID + if src == "" { + return nil + } + + // see Parse for required string format + u, err := Parse(src) + if err != nil { + return fmt.Errorf("Scan: %v", err) + } + + *uuid = u + + case []byte: + // if an empty UUID comes from a table, we return a null UUID + if len(src) == 0 { + return nil + } + + // assumes a simple slice of bytes if 16 bytes + // otherwise attempts to parse + if len(src) != 16 { + return uuid.Scan(string(src)) + } + copy((*uuid)[:], src) + + default: + return fmt.Errorf("Scan: unable to scan type %T into UUID", src) + } + + return nil +} + +// Value implements sql.Valuer so that UUIDs can be written to databases +// transparently. Currently, UUIDs map to strings. Please consult +// database-specific driver documentation for matching types. +func (uuid UUID) Value() (driver.Value, error) { + return uuid.String(), nil +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/time.go b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/time.go new file mode 100644 index 00000000..fd7fe0ac --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/time.go @@ -0,0 +1,123 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "sync" + "time" +) + +// A Time represents a time as the number of 100's of nanoseconds since 15 Oct +// 1582. +type Time int64 + +const ( + lillian = 2299160 // Julian day of 15 Oct 1582 + unix = 2440587 // Julian day of 1 Jan 1970 + epoch = unix - lillian // Days between epochs + g1582 = epoch * 86400 // seconds between epochs + g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs +) + +var ( + timeMu sync.Mutex + lasttime uint64 // last time we returned + clockSeq uint16 // clock sequence for this run + + timeNow = time.Now // for testing +) + +// UnixTime converts t the number of seconds and nanoseconds using the Unix +// epoch of 1 Jan 1970. +func (t Time) UnixTime() (sec, nsec int64) { + sec = int64(t - g1582ns100) + nsec = (sec % 10000000) * 100 + sec /= 10000000 + return sec, nsec +} + +// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and +// clock sequence as well as adjusting the clock sequence as needed. An error +// is returned if the current time cannot be determined. +func GetTime() (Time, uint16, error) { + defer timeMu.Unlock() + timeMu.Lock() + return getTime() +} + +func getTime() (Time, uint16, error) { + t := timeNow() + + // If we don't have a clock sequence already, set one. + if clockSeq == 0 { + setClockSequence(-1) + } + now := uint64(t.UnixNano()/100) + g1582ns100 + + // If time has gone backwards with this clock sequence then we + // increment the clock sequence + if now <= lasttime { + clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000 + } + lasttime = now + return Time(now), clockSeq, nil +} + +// ClockSequence returns the current clock sequence, generating one if not +// already set. The clock sequence is only used for Version 1 UUIDs. +// +// The uuid package does not use global static storage for the clock sequence or +// the last time a UUID was generated. Unless SetClockSequence is used, a new +// random clock sequence is generated the first time a clock sequence is +// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) +func ClockSequence() int { + defer timeMu.Unlock() + timeMu.Lock() + return clockSequence() +} + +func clockSequence() int { + if clockSeq == 0 { + setClockSequence(-1) + } + return int(clockSeq & 0x3fff) +} + +// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to +// -1 causes a new sequence to be generated. +func SetClockSequence(seq int) { + defer timeMu.Unlock() + timeMu.Lock() + setClockSequence(seq) +} + +func setClockSequence(seq int) { + if seq == -1 { + var b [2]byte + randomBits(b[:]) // clock sequence + seq = int(b[0])<<8 | int(b[1]) + } + old_seq := clockSeq + clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant + if old_seq != clockSeq { + lasttime = 0 + } +} + +// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in +// uuid. The time is only defined for version 1 and 2 UUIDs. +func (uuid UUID) Time() Time { + time := int64(binary.BigEndian.Uint32(uuid[0:4])) + time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 + time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 + return Time(time) +} + +// ClockSequence returns the clock sequence encoded in uuid. +// The clock sequence is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) ClockSequence() int { + return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/util.go b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/util.go new file mode 100644 index 00000000..5ea6c737 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/util.go @@ -0,0 +1,43 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// randomBits completely fills slice b with random data. +func randomBits(b []byte) { + if _, err := io.ReadFull(rander, b); err != nil { + panic(err.Error()) // rand should never fail + } +} + +// xvalues returns the value of a byte as a hexadecimal digit or 255. +var xvalues = [256]byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +} + +// xtob converts hex characters x1 and x2 into a byte. +func xtob(x1, x2 byte) (byte, bool) { + b1 := xvalues[x1] + b2 := xvalues[x2] + return (b1 << 4) | b2, b1 != 255 && b2 != 255 +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/uuid.go new file mode 100644 index 00000000..b7b9ced3 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/uuid.go @@ -0,0 +1,191 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "errors" + "fmt" + "io" + "strings" +) + +// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC +// 4122. +type UUID [16]byte + +// A Version represents a UUID's version. +type Version byte + +// A Variant represents a UUID's variant. +type Variant byte + +// Constants returned by Variant. +const ( + Invalid = Variant(iota) // Invalid UUID + RFC4122 // The variant specified in RFC4122 + Reserved // Reserved, NCS backward compatibility. + Microsoft // Reserved, Microsoft Corporation backward compatibility. + Future // Reserved for future definition. +) + +var rander = rand.Reader // random function + +// Parse decodes s into a UUID or returns an error. Both the UUID form of +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded. +func Parse(s string) (UUID, error) { + var uuid UUID + if len(s) != 36 { + if len(s) != 36+9 { + return uuid, fmt.Errorf("invalid UUID length: %d", len(s)) + } + if strings.ToLower(s[:9]) != "urn:uuid:" { + return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) + } + s = s[9:] + } + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return uuid, errors.New("invalid UUID format") + } + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34} { + if v, ok := xtob(s[x], s[x+1]); !ok { + return uuid, errors.New("invalid UUID format") + } else { + uuid[i] = v + } + } + return uuid, nil +} + +// ParseBytes is like Parse, except it parses a byte slice instead of a string. +func ParseBytes(b []byte) (UUID, error) { + var uuid UUID + if len(b) != 36 { + if len(b) != 36+9 { + return uuid, fmt.Errorf("invalid UUID length: %d", len(b)) + } + if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) { + return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) + } + b = b[9:] + } + if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { + return uuid, errors.New("invalid UUID format") + } + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34} { + if v, ok := xtob(b[x], b[x+1]); !ok { + return uuid, errors.New("invalid UUID format") + } else { + uuid[i] = v + } + } + return uuid, nil +} + +// Must returns uuid if err is nil and panics otherwise. +func Must(uuid UUID, err error) UUID { + if err != nil { + panic(err) + } + return uuid +} + +// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// , or "" if uuid is invalid. +func (uuid UUID) String() string { + var buf [36]byte + encodeHex(buf[:], uuid) + return string(buf[:]) +} + +// URN returns the RFC 2141 URN form of uuid, +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. +func (uuid UUID) URN() string { + var buf [36 + 9]byte + copy(buf[:], "urn:uuid:") + encodeHex(buf[9:], uuid) + return string(buf[:]) +} + +func encodeHex(dst []byte, uuid UUID) { + hex.Encode(dst[:], uuid[:4]) + dst[8] = '-' + hex.Encode(dst[9:13], uuid[4:6]) + dst[13] = '-' + hex.Encode(dst[14:18], uuid[6:8]) + dst[18] = '-' + hex.Encode(dst[19:23], uuid[8:10]) + dst[23] = '-' + hex.Encode(dst[24:], uuid[10:]) +} + +// Variant returns the variant encoded in uuid. +func (uuid UUID) Variant() Variant { + switch { + case (uuid[8] & 0xc0) == 0x80: + return RFC4122 + case (uuid[8] & 0xe0) == 0xc0: + return Microsoft + case (uuid[8] & 0xe0) == 0xe0: + return Future + default: + return Reserved + } +} + +// Version returns the version of uuid. +func (uuid UUID) Version() Version { + return Version(uuid[6] >> 4) +} + +func (v Version) String() string { + if v > 15 { + return fmt.Sprintf("BAD_VERSION_%d", v) + } + return fmt.Sprintf("VERSION_%d", v) +} + +func (v Variant) String() string { + switch v { + case RFC4122: + return "RFC4122" + case Reserved: + return "Reserved" + case Microsoft: + return "Microsoft" + case Future: + return "Future" + case Invalid: + return "Invalid" + } + return fmt.Sprintf("BadVariant%d", int(v)) +} + +// SetRand sets the random number generator to r, which implents io.Reader. +// If r.Read returns an error when the package requests random data then +// a panic will be issued. +// +// Calling SetRand with nil sets the random number generator to the default +// generator. +func SetRand(r io.Reader) { + if r == nil { + rander = rand.Reader + return + } + rander = r +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/version1.go b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/version1.go new file mode 100644 index 00000000..22dc07cd --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/version1.go @@ -0,0 +1,44 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" +) + +// NewUUID returns a Version 1 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewUUID returns nil. If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewUUID returns Nil and an error. +// +// In most cases, New should be used. +func NewUUID() (UUID, error) { + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + nodeMu.Unlock() + + var uuid UUID + now, seq, err := GetTime() + if err != nil { + return uuid, err + } + + timeLow := uint32(now & 0xffffffff) + timeMid := uint16((now >> 32) & 0xffff) + timeHi := uint16((now >> 48) & 0x0fff) + timeHi |= 0x1000 // Version 1 + + binary.BigEndian.PutUint32(uuid[0:], timeLow) + binary.BigEndian.PutUint16(uuid[4:], timeMid) + binary.BigEndian.PutUint16(uuid[6:], timeHi) + binary.BigEndian.PutUint16(uuid[8:], seq) + copy(uuid[10:], nodeID[:]) + + return uuid, nil +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/version4.go b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/version4.go new file mode 100644 index 00000000..390dd2ca --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/google/uuid/version4.go @@ -0,0 +1,38 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "io" + +// New is creates a new random UUID or panics. New is equivalent to +// the expression +// +// uuid.Must(uuid.NewRandom()) +func New() UUID { + return Must(NewRandom()) +} + +// NewRandom returns a Random (Version 4) UUID or panics. +// +// The strength of the UUIDs is based on the strength of the crypto/rand +// package. +// +// A note about uniqueness derived from from the UUID Wikipedia entry: +// +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. +func NewRandom() (UUID, error) { + var uuid UUID + _, err := io.ReadFull(rander, uuid[:]) + if err != nil { + return Nil, err + } + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor.go new file mode 100644 index 00000000..43ee14cf --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor.go @@ -0,0 +1,104 @@ +// Copyright 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 bdoor + +const ( + BackdoorPort = uint16(0x5658) + BackdoorHighBWPort = uint16(0x5659) + + CommandGetVersion = uint32(10) + + CommandMessage = uint16(0x1e) + CommandHighBWMessage = uint16(0) + CommandFlagCookie = uint32(0x80000000) +) + +func (p *BackdoorProto) InOut() *BackdoorProto { + p.DX.AsUInt32().Low = BackdoorPort + p.AX.SetValue(BackdoorMagic) + + retax, retbx, retcx, retdx, retsi, retdi, retbp := bdoor_inout( + p.AX.Value(), + p.BX.Value(), + p.CX.Value(), + p.DX.Value(), + p.SI.Value(), + p.DI.Value(), + p.BP.Value(), + ) + + ret := &BackdoorProto{} + ret.AX.SetValue(retax) + ret.BX.SetValue(retbx) + ret.CX.SetValue(retcx) + ret.DX.SetValue(retdx) + ret.SI.SetValue(retsi) + ret.DI.SetValue(retdi) + ret.BP.SetValue(retbp) + + return ret +} + +func (p *BackdoorProto) HighBandwidthOut() *BackdoorProto { + p.DX.AsUInt32().Low = BackdoorHighBWPort + p.AX.SetValue(BackdoorMagic) + + retax, retbx, retcx, retdx, retsi, retdi, retbp := bdoor_hbout( + p.AX.Value(), + p.BX.Value(), + p.CX.Value(), + p.DX.Value(), + p.SI.Value(), + p.DI.Value(), + p.BP.Value(), + ) + + ret := &BackdoorProto{} + ret.AX.SetValue(retax) + ret.BX.SetValue(retbx) + ret.CX.SetValue(retcx) + ret.DX.SetValue(retdx) + ret.SI.SetValue(retsi) + ret.DI.SetValue(retdi) + ret.BP.SetValue(retbp) + + return ret +} + +func (p *BackdoorProto) HighBandwidthIn() *BackdoorProto { + p.DX.AsUInt32().Low = BackdoorHighBWPort + p.AX.SetValue(BackdoorMagic) + + retax, retbx, retcx, retdx, retsi, retdi, retbp := bdoor_hbin( + p.AX.Value(), + p.BX.Value(), + p.CX.Value(), + p.DX.Value(), + p.SI.Value(), + p.DI.Value(), + p.BP.Value(), + ) + + ret := &BackdoorProto{} + ret.AX.SetValue(retax) + ret.BX.SetValue(retbx) + ret.CX.SetValue(retcx) + ret.DX.SetValue(retdx) + ret.SI.SetValue(retsi) + ret.DI.SetValue(retdi) + ret.BP.SetValue(retbp) + + return ret +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor_386.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor_386.go new file mode 100644 index 00000000..ea9a2f25 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor_386.go @@ -0,0 +1,48 @@ +// Copyright 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 bdoor + +const ( + BackdoorMagic = uint32(0x564D5868) +) + +type BackdoorProto struct { + // typedef union { + // struct { + // DECLARE_REG_NAMED_STRUCT(ax); + // size_t size; /* Register bx. */ + // DECLARE_REG_NAMED_STRUCT(cx); + // DECLARE_REG_NAMED_STRUCT(dx); + // DECLARE_REG_NAMED_STRUCT(si); + // DECLARE_REG_NAMED_STRUCT(di); + // } in; + // struct { + // DECLARE_REG_NAMED_STRUCT(ax); + // DECLARE_REG_NAMED_STRUCT(bx); + // DECLARE_REG_NAMED_STRUCT(cx); + // DECLARE_REG_NAMED_STRUCT(dx); + // DECLARE_REG_NAMED_STRUCT(si); + // DECLARE_REG_NAMED_STRUCT(di); + // } out; + // } proto; + + AX, BX, CX, DX, SI, DI, BP UInt32 + size uint32 +} + +func bdoor_inout(ax, bx, cx, dx, si, di, bp uint32) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint32) +func bdoor_hbout(ax, bx, cx, dx, si, di, bp uint32) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint32) +func bdoor_hbin(ax, bx, cx, dx, si, di, bp uint32) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint32) +func bdoor_inout_test(ax, bx, cx, dx, si, di, bp uint32) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint32) diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor_386.s b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor_386.s new file mode 100644 index 00000000..a6e11b11 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor_386.s @@ -0,0 +1,112 @@ +#include "textflag.h" + +// Doc of the golang plan9 assembler +// http://p9.nyx.link/labs/sys/doc/asm.html +// +// A good primer of how to write golang with some plan9 flavored assembly +// http://www.doxsey.net/blog/go-and-assembly +// +// Some x86 references +// http://www.eecg.toronto.edu/~amza/www.mindsec.com/files/x86regs.html +// https://cseweb.ucsd.edu/classes/sp10/cse141/pdf/02/S01_x86_64.key.pdf +// https://en.wikibooks.org/wiki/X86_Assembly/Other_Instructions +// +// (This one is invaluable. Has a working example of how a standard function +// call looks on the stack with the associated assembly.) +// https://www.recurse.com/blog/7-understanding-c-by-learning-assembly +// +// Reference with raw form of the Opcode +// http://x86.renejeschke.de/html/file_module_x86_id_139.html +// +// Massive x86_64 reference +// http://ref.x86asm.net/coder64.html#xED +// +// Adding instructions to the go assembler +// https://blog.klauspost.com/adding-unsupported-instructions-in-golang-assembler/ +// +// Backdoor commands +// https://sites.google.com/site/chitchatvmback/backdoor + +// func bdoor_inout(ax, bx, cx, dx, si, di, bp uint32) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint32) +TEXT ·bdoor_inout(SB), NOSPLIT|WRAPPER, $0 + MOVL ax+0(FP), AX + MOVL bx+4(FP), BX + MOVL cx+8(FP), CX + MOVL dx+12(FP), DX + MOVL si+16(FP), SI + MOVL di+20(FP), DI + MOVL bp+24(FP), BP + + // IN to DX from EAX + INL + + MOVL AX, retax+28(FP) + MOVL BX, retbx+32(FP) + MOVL CX, retcx+36(FP) + MOVL DX, retdx+40(FP) + MOVL SI, retsi+44(FP) + MOVL DI, retdi+48(FP) + MOVL BP, retbp+52(FP) + RET + +// func bdoor_hbout(ax, bx, cx, dx, si, di, bp uint32) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint32) +TEXT ·bdoor_hbout(SB), NOSPLIT|WRAPPER, $0 + MOVL ax+0(FP), AX + MOVL bx+4(FP), BX + MOVL cx+8(FP), CX + MOVL dx+12(FP), DX + MOVL si+16(FP), SI + MOVL di+20(FP), DI + MOVL bp+24(FP), BP + + CLD; REP; OUTSB + + MOVL AX, retax+28(FP) + MOVL BX, retbx+32(FP) + MOVL CX, retcx+36(FP) + MOVL DX, retdx+40(FP) + MOVL SI, retsi+44(FP) + MOVL DI, retdi+48(FP) + MOVL BP, retbp+52(FP) + RET + +// func bdoor_hbin(ax, bx, cx, dx, si, di, bp uint32) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint32) +TEXT ·bdoor_hbin(SB), NOSPLIT|WRAPPER, $0 + MOVL ax+0(FP), AX + MOVL bx+4(FP), BX + MOVL cx+8(FP), CX + MOVL dx+12(FP), DX + MOVL si+16(FP), SI + MOVL di+20(FP), DI + MOVL bp+24(FP), BP + + CLD; REP; INSB + + MOVL AX, retax+28(FP) + MOVL BX, retbx+32(FP) + MOVL CX, retcx+40(FP) + MOVL DX, retdx+44(FP) + MOVL SI, retsi+48(FP) + MOVL DI, retdi+52(FP) + MOVL BP, retbp+56(FP) + RET + +// func bdoor_inout_test(ax, bx, cx, dx, si, di, bp uint32) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint32) +TEXT ·bdoor_inout_test(SB), NOSPLIT|WRAPPER, $0 + MOVL ax+0(FP), AX + MOVL bx+4(FP), BX + MOVL cx+8(FP), CX + MOVL dx+12(FP), DX + MOVL si+16(FP), SI + MOVL di+20(FP), DI + MOVL bp+24(FP), BP + + MOVL AX, retax+28(FP) + MOVL BX, retbx+32(FP) + MOVL CX, retcx+36(FP) + MOVL DX, retdx+40(FP) + MOVL SI, retsi+44(FP) + MOVL DI, retdi+48(FP) + MOVL BP, retbp+52(FP) + RET + diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor_amd64.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor_amd64.go new file mode 100644 index 00000000..0793c699 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor_amd64.go @@ -0,0 +1,48 @@ +// Copyright 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 bdoor + +const ( + BackdoorMagic = uint64(0x564D5868) +) + +type BackdoorProto struct { + // typedef union { + // struct { + // DECLARE_REG_NAMED_STRUCT(ax); + // size_t size; /* Register bx. */ + // DECLARE_REG_NAMED_STRUCT(cx); + // DECLARE_REG_NAMED_STRUCT(dx); + // DECLARE_REG_NAMED_STRUCT(si); + // DECLARE_REG_NAMED_STRUCT(di); + // } in; + // struct { + // DECLARE_REG_NAMED_STRUCT(ax); + // DECLARE_REG_NAMED_STRUCT(bx); + // DECLARE_REG_NAMED_STRUCT(cx); + // DECLARE_REG_NAMED_STRUCT(dx); + // DECLARE_REG_NAMED_STRUCT(si); + // DECLARE_REG_NAMED_STRUCT(di); + // } out; + // } proto; + + AX, BX, CX, DX, SI, DI, BP UInt64 + size uint32 +} + +func bdoor_inout(ax, bx, cx, dx, si, di, bp uint64) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint64) +func bdoor_hbout(ax, bx, cx, dx, si, di, bp uint64) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint64) +func bdoor_hbin(ax, bx, cx, dx, si, di, bp uint64) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint64) +func bdoor_inout_test(ax, bx, cx, dx, si, di, bp uint64) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint64) diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor_amd64.s b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor_amd64.s new file mode 100644 index 00000000..62f0c06d --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/bdoor_amd64.s @@ -0,0 +1,112 @@ +#include "textflag.h" + +// Doc of the golang plan9 assembler +// http://p9.nyx.link/labs/sys/doc/asm.html +// +// A good primer of how to write golang with some plan9 flavored assembly +// http://www.doxsey.net/blog/go-and-assembly +// +// Some x86 references +// http://www.eecg.toronto.edu/~amza/www.mindsec.com/files/x86regs.html +// https://cseweb.ucsd.edu/classes/sp10/cse141/pdf/02/S01_x86_64.key.pdf +// https://en.wikibooks.org/wiki/X86_Assembly/Other_Instructions +// +// (This one is invaluable. Has a working example of how a standard function +// call looks on the stack with the associated assembly.) +// https://www.recurse.com/blog/7-understanding-c-by-learning-assembly +// +// Reference with raw form of the Opcode +// http://x86.renejeschke.de/html/file_module_x86_id_139.html +// +// Massive x86_64 reference +// http://ref.x86asm.net/coder64.html#xED +// +// Adding instructions to the go assembler +// https://blog.klauspost.com/adding-unsupported-instructions-in-golang-assembler/ +// +// Backdoor commands +// https://sites.google.com/site/chitchatvmback/backdoor + +// func bdoor_inout(ax, bx, cx, dx, si, di, bp uint64) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint64) +TEXT ·bdoor_inout(SB), NOSPLIT|WRAPPER, $0 + MOVQ ax+0(FP), AX + MOVQ bx+8(FP), BX + MOVQ cx+16(FP), CX + MOVQ dx+24(FP), DX + MOVQ si+32(FP), SI + MOVQ di+40(FP), DI + MOVQ bp+48(FP), BP + + // IN to DX from EAX + INL + + MOVQ AX, retax+56(FP) + MOVQ BX, retbx+64(FP) + MOVQ CX, retcx+72(FP) + MOVQ DX, retdx+80(FP) + MOVQ SI, retsi+88(FP) + MOVQ DI, retdi+96(FP) + MOVQ BP, retbp+104(FP) + RET + +// func bdoor_hbout(ax, bx, cx, dx, si, di, bp uint64) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint64) +TEXT ·bdoor_hbout(SB), NOSPLIT|WRAPPER, $0 + MOVQ ax+0(FP), AX + MOVQ bx+8(FP), BX + MOVQ cx+16(FP), CX + MOVQ dx+24(FP), DX + MOVQ si+32(FP), SI + MOVQ di+40(FP), DI + MOVQ bp+48(FP), BP + + CLD; REP; OUTSB + + MOVQ AX, retax+56(FP) + MOVQ BX, retbx+64(FP) + MOVQ CX, retcx+72(FP) + MOVQ DX, retdx+80(FP) + MOVQ SI, retsi+88(FP) + MOVQ DI, retdi+96(FP) + MOVQ BP, retbp+104(FP) + RET + +// func bdoor_hbin(ax, bx, cx, dx, si, di, bp uint64) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint64) +TEXT ·bdoor_hbin(SB), NOSPLIT|WRAPPER, $0 + MOVQ ax+0(FP), AX + MOVQ bx+8(FP), BX + MOVQ cx+16(FP), CX + MOVQ dx+24(FP), DX + MOVQ si+32(FP), SI + MOVQ di+40(FP), DI + MOVQ bp+48(FP), BP + + CLD; REP; INSB + + MOVQ AX, retax+56(FP) + MOVQ BX, retbx+64(FP) + MOVQ CX, retcx+72(FP) + MOVQ DX, retdx+80(FP) + MOVQ SI, retsi+88(FP) + MOVQ DI, retdi+96(FP) + MOVQ BP, retbp+104(FP) + RET + +// func bdoor_inout_test(ax, bx, cx, dx, si, di, bp uint64) (retax, retbx, retcx, retdx, retsi, retdi, retbp uint64) +TEXT ·bdoor_inout_test(SB), NOSPLIT|WRAPPER, $0 + MOVQ ax+0(FP), AX + MOVQ bx+8(FP), BX + MOVQ cx+16(FP), CX + MOVQ dx+24(FP), DX + MOVQ si+32(FP), SI + MOVQ di+40(FP), DI + MOVQ bp+48(FP), BP + + MOVQ AX, retax+56(FP) + MOVQ BX, retbx+64(FP) + MOVQ CX, retcx+72(FP) + MOVQ DX, retdx+80(FP) + MOVQ SI, retsi+88(FP) + MOVQ DI, retdi+96(FP) + MOVQ BP, retbp+104(FP) + RET + diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/word.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/word.go new file mode 100644 index 00000000..ec1ee13b --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/bdoor/word.go @@ -0,0 +1,77 @@ +// Copyright 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 bdoor + +import "unsafe" + +type UInt32 struct { + High uint16 + Low uint16 +} + +func (u *UInt32) Word() uint32 { + return uint32(u.High)<<16 + uint32(u.Low) +} + +func (u *UInt32) SetWord(w uint32) { + u.High = uint16(w >> 16) + u.Low = uint16(w) +} + +func (u *UInt32) AsUInt32() *UInt32 { + return u +} + +func (u *UInt32) Value() uint32 { + return u.Word() +} + +func (u *UInt32) SetValue(val uint32) { + u.SetWord(val) +} + +func (u *UInt32) SetPointer(p unsafe.Pointer) { + u.SetWord(uint32(uintptr(p))) +} + +type UInt64 struct { + High UInt32 + Low UInt32 +} + +func (u *UInt64) Quad() uint64 { + return uint64(u.High.Word())<<32 + uint64(u.Low.Word()) +} + +func (u *UInt64) SetQuad(w uint64) { + u.High.SetWord(uint32(w >> 32)) + u.Low.SetWord(uint32(w)) +} + +func (u *UInt64) AsUInt32() *UInt32 { + return &u.Low +} + +func (u *UInt64) Value() uint64 { + return u.Quad() +} + +func (u *UInt64) SetValue(val uint64) { + u.SetQuad(val) +} + +func (u *UInt64) SetPointer(p unsafe.Pointer) { + u.SetQuad(uint64(uintptr(p))) +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/examples/main.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/examples/main.go new file mode 100644 index 00000000..6658020c --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/examples/main.go @@ -0,0 +1,75 @@ +// Copyright 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 main + +import ( + "flag" + "fmt" + "log" + + "github.com/vmware/vmw-guestinfo/rpcvmx" + "github.com/vmware/vmw-guestinfo/vmcheck" +) + +var ( + set bool + get bool +) + +func init() { + + flag.BoolVar(&set, "set", false, "Sets the guestinfo.KEY with the string VALUE") + flag.BoolVar(&get, "get", false, "Returns the config string in the guestinfo.* namespace") + + flag.Parse() +} + +func main() { + + isVM, err := vmcheck.IsVirtualWorld() + if err != nil { + log.Fatalf("Error: %s", err) + } + + if !isVM { + log.Fatalf("ERROR: not in a virtual world.") + } + + if !set && !get { + flag.Usage() + } + + config := rpcvmx.NewConfig() + if set { + if flag.NArg() != 2 { + log.Fatalf("ERROR: Please provide guestinfo key / value pair (eg; -set foo bar") + } + if err := config.SetString(flag.Arg(0), flag.Arg(1)); err != nil { + log.Fatalf("ERROR: SetString failed with %s", err) + } + } + + if get { + if flag.NArg() != 1 { + log.Fatalf("ERROR: Please provide guestinfo key (eg; -get foo)") + } + if out, err := config.String(flag.Arg(0), ""); err != nil { + log.Fatalf("ERROR: String failed with %s", err) + } else { + fmt.Printf("%s\n", out) + } + } + +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/message/log.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/message/log.go new file mode 100644 index 00000000..3bb96f2e --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/message/log.go @@ -0,0 +1,61 @@ +// Copyright 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 message + +import "log" + +var DefaultLogger Logger + +type Logger interface { + Errorf(format string, args ...interface{}) + Debugf(format string, args ...interface{}) + Infof(format string, args ...interface{}) +} + +func init() { + DefaultLogger = &logger{} +} + +type logger struct { + DebugLevel bool +} + +func (l *logger) Errorf(format string, args ...interface{}) { + log.Printf(format, args...) +} + +func (l *logger) Debugf(format string, args ...interface{}) { + if !l.DebugLevel { + return + } + + log.Printf(format, args...) +} + +func (l *logger) Infof(format string, args ...interface{}) { + log.Printf(format, args...) +} + +func Errorf(format string, args ...interface{}) { + DefaultLogger.Errorf(format, args...) +} + +func Debugf(format string, args ...interface{}) { + DefaultLogger.Debugf(format, args...) +} + +func Infof(format string, args ...interface{}) { + DefaultLogger.Infof(format, args...) +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/message/message.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/message/message.go new file mode 100644 index 00000000..a6261920 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/message/message.go @@ -0,0 +1,334 @@ +// Copyright 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 message + +import ( + "bytes" + "encoding/binary" + "errors" + "unsafe" + + "github.com/vmware/vmw-guestinfo/bdoor" +) + +const ( + messageTypeOpen = iota + messageTypeSendSize + messageTypeSendPayload + messageTypeReceiveSize + messageTypeReceivePayload + messageTypeReceiveStatus + messageTypeClose + + messageStatusFail = uint16(0x0000) + messageStatusSuccess = uint16(0x0001) + messageStatusDoRecieve = uint16(0x0002) + messageStatusCheckPoint = uint16(0x0010) + messageStatusHighBW = uint16(0x0080) +) + +var ( + // ErrChannelOpen represents a failure to open a channel + ErrChannelOpen = errors.New("could not open channel") + // ErrChannelClose represents a failure to close a channel + ErrChannelClose = errors.New("could not close channel") + // ErrRpciSend represents a failure to send a message + ErrRpciSend = errors.New("unable to send RPCI command") + // ErrRpciReceive represents a failure to receive a message + ErrRpciReceive = errors.New("unable to receive RPCI command result") +) + +type Channel struct { + id uint16 + + forceLowBW bool + buf []byte + + cookie bdoor.UInt64 +} + +// NewChannel opens a new Channel +func NewChannel(proto uint32) (*Channel, error) { + flags := bdoor.CommandFlagCookie + +retry: + bp := &bdoor.BackdoorProto{} + + bp.BX.AsUInt32().SetWord(proto | flags) + bp.CX.AsUInt32().High = messageTypeOpen + bp.CX.AsUInt32().Low = bdoor.CommandMessage + + out := bp.InOut() + if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 { + if flags != 0 { + flags = 0 + goto retry + } + + Errorf("Message: Unable to open communication channel") + return nil, ErrChannelOpen + } + + ch := &Channel{} + ch.id = out.DX.AsUInt32().High + ch.cookie.High.SetWord(out.SI.AsUInt32().Word()) + ch.cookie.Low.SetWord(out.DI.AsUInt32().Word()) + + Debugf("Opened channel %d", ch.id) + return ch, nil +} + +func (c *Channel) Close() error { + bp := &bdoor.BackdoorProto{} + + bp.CX.AsUInt32().High = messageTypeClose + bp.CX.AsUInt32().Low = bdoor.CommandMessage + + bp.DX.AsUInt32().High = c.id + bp.SI.AsUInt32().SetWord(c.cookie.High.Word()) + bp.DI.AsUInt32().SetWord(c.cookie.Low.Word()) + + out := bp.InOut() + if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 { + Errorf("Message: Unable to close communication channel %d", c.id) + return ErrChannelClose + } + + Debugf("Closed channel %d", c.id) + return nil +} + +func (c *Channel) Send(buf []byte) error { +retry: + bp := &bdoor.BackdoorProto{} + bp.CX.AsUInt32().High = messageTypeSendSize + bp.CX.AsUInt32().Low = bdoor.CommandMessage + + bp.DX.AsUInt32().High = c.id + bp.SI.AsUInt32().SetWord(c.cookie.High.Word()) + bp.DI.AsUInt32().SetWord(c.cookie.Low.Word()) + + bp.BX.AsUInt32().SetWord(uint32(len(buf))) + + // send the size + out := bp.InOut() + if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 { + Errorf("Message: Unable to send a message over the communication channel %d", c.id) + return ErrRpciSend + } + + // size of buf 0 is fine, just return + if len(buf) == 0 { + return nil + } + + if !c.forceLowBW && (out.CX.AsUInt32().High&messageStatusHighBW) == messageStatusHighBW { + hbbp := &bdoor.BackdoorProto{} + + hbbp.BX.AsUInt32().Low = bdoor.CommandHighBWMessage + hbbp.BX.AsUInt32().High = messageStatusSuccess + hbbp.DX.AsUInt32().High = c.id + hbbp.BP.AsUInt32().SetWord(c.cookie.High.Word()) + hbbp.DI.AsUInt32().SetWord(c.cookie.Low.Word()) + hbbp.CX.AsUInt32().SetWord(uint32(len(buf))) + hbbp.SI.SetPointer(unsafe.Pointer(&buf[0])) + + out := hbbp.HighBandwidthOut() + if (out.BX.AsUInt32().High & messageStatusSuccess) == 0 { + if (out.BX.AsUInt32().High & messageStatusCheckPoint) != 0 { + Debugf("A checkpoint occurred. Retrying the operation") + goto retry + } + + Errorf("Message: Unable to send a message over the communication channel %d", c.id) + return ErrRpciSend + } + } else { + bp.CX.AsUInt32().High = messageTypeSendPayload + + bbuf := bytes.NewBuffer(buf) + for { + // read 4 bytes at a time + words := bbuf.Next(4) + if len(words) == 0 { + break + } + + Debugf("sending %q over %d", string(words), c.id) + switch len(words) { + case 3: + bp.BX.AsUInt32().SetWord(binary.LittleEndian.Uint32([]byte{0x0, words[2], words[1], words[0]})) + case 2: + bp.BX.AsUInt32().SetWord(uint32(binary.LittleEndian.Uint16(words))) + case 1: + bp.BX.AsUInt32().SetWord(uint32(words[0])) + default: + bp.BX.AsUInt32().SetWord(binary.LittleEndian.Uint32(words)) + } + + out = bp.InOut() + if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 { + Errorf("Message: Unable to send a message over the communication channel %d", c.id) + return ErrRpciSend + } + } + } + + return nil +} + +func (c *Channel) Receive() ([]byte, error) { +retry: + var err error + bp := &bdoor.BackdoorProto{} + bp.CX.AsUInt32().High = messageTypeReceiveSize + bp.CX.AsUInt32().Low = bdoor.CommandMessage + + bp.DX.AsUInt32().High = c.id + bp.SI.AsUInt32().SetWord(c.cookie.High.Word()) + bp.DI.AsUInt32().SetWord(c.cookie.Low.Word()) + + out := bp.InOut() + if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 { + Errorf("Message: Unable to poll for messages over the communication channel %d", c.id) + return nil, ErrRpciReceive + } + + if (out.CX.AsUInt32().High & messageStatusDoRecieve) == 0 { + Debugf("No message to retrieve") + return nil, nil + } + + // Receive the size. + if out.DX.AsUInt32().High != messageTypeSendSize { + Errorf("Message: Protocol error. Expected a MESSAGE_TYPE_SENDSIZE request from vmware") + return nil, ErrRpciReceive + } + + size := out.BX.Value() + + var buf []byte + + if size != 0 { + if !c.forceLowBW && (out.CX.AsUInt32().High&messageStatusHighBW == messageStatusHighBW) { + buf = make([]byte, size) + + hbbp := &bdoor.BackdoorProto{} + + hbbp.BX.AsUInt32().Low = bdoor.CommandHighBWMessage + hbbp.BX.AsUInt32().High = messageStatusSuccess + hbbp.DX.AsUInt32().High = c.id + hbbp.SI.AsUInt32().SetWord(c.cookie.High.Word()) + hbbp.BP.AsUInt32().SetWord(c.cookie.Low.Word()) + hbbp.CX.AsUInt32().SetWord(uint32(len(buf))) + hbbp.DI.SetPointer(unsafe.Pointer(&buf[0])) + + out := hbbp.HighBandwidthIn() + if (out.BX.AsUInt32().High & messageStatusSuccess) == 0 { + Errorf("Message: Unable to send a message over the communication channel %d", c.id) + c.reply(messageTypeReceivePayload, messageStatusFail) + return nil, ErrRpciReceive + } + } else { + b := bytes.NewBuffer(make([]byte, 0, size)) + + for { + if size == 0 { + break + } + + bp.CX.AsUInt32().High = messageTypeReceivePayload + bp.BX.AsUInt32().Low = messageStatusSuccess + + out = bp.InOut() + if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 { + if (out.CX.AsUInt32().High & messageStatusCheckPoint) != 0 { + Debugf("A checkpoint occurred. Retrying the operation") + goto retry + } + + Errorf("Message: Unable to receive a message over the communication channel %d", c.id) + c.reply(messageTypeReceivePayload, messageStatusFail) + return nil, ErrRpciReceive + } + + if out.DX.AsUInt32().High != messageTypeSendPayload { + Errorf("Message: Protocol error. Expected a MESSAGE_TYPE_SENDPAYLOAD from vmware") + c.reply(messageTypeReceivePayload, messageStatusFail) + return nil, ErrRpciReceive + } + + Debugf("Received %#v", out.BX.AsUInt32().Word()) + + switch size { + case 1: + err = binary.Write(b, binary.LittleEndian, uint8(out.BX.AsUInt32().Low)) + size = size - 1 + + case 2: + err = binary.Write(b, binary.LittleEndian, uint16(out.BX.AsUInt32().Low)) + size = size - 2 + + case 3: + err = binary.Write(b, binary.LittleEndian, uint16(out.BX.AsUInt32().Low)) + if err != nil { + c.reply(messageTypeReceivePayload, messageStatusFail) + return nil, ErrRpciReceive + } + err = binary.Write(b, binary.LittleEndian, uint8(out.BX.AsUInt32().High)) + size = size - 3 + + default: + err = binary.Write(b, binary.LittleEndian, out.BX.AsUInt32().Word()) + size = size - 4 + } + + if err != nil { + Errorf(err.Error()) + c.reply(messageTypeReceivePayload, messageStatusFail) + return nil, ErrRpciReceive + } + } + + buf = b.Bytes() + } + } + + c.reply(messageTypeReceiveStatus, messageStatusSuccess) + + return buf, nil +} + +func (c *Channel) reply(messageType, messageStatus uint16) { + bp := &bdoor.BackdoorProto{} + + bp.BX.AsUInt32().Low = messageStatus + bp.CX.AsUInt32().High = messageType + bp.CX.AsUInt32().Low = bdoor.CommandMessage + bp.DX.AsUInt32().High = c.id + bp.SI.AsUInt32().SetWord(c.cookie.High.Word()) + bp.DI.AsUInt32().SetWord(c.cookie.Low.Word()) + + out := bp.InOut() + + /* OUT: Status */ + if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 { + if messageStatus == messageStatusSuccess { + Errorf("reply Message: Unable to send a message over the communication channel %d", c.id) + } else { + Errorf("reply Message: Unable to signal an error of reception over the communication channel %d", c.id) + } + } +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/rpcout/rpcout.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/rpcout/rpcout.go new file mode 100644 index 00000000..3b5879b4 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/rpcout/rpcout.go @@ -0,0 +1,93 @@ +// Copyright 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 rpcout + +import ( + "errors" + "fmt" + + "github.com/vmware/vmw-guestinfo/message" +) + +// ErrRpciFormat represents an invalid result format +var ErrRpciFormat = errors.New("invalid format for RPCI command result") + +const rpciProtocolNum uint32 = 0x49435052 + +// SendOne is a command-oriented wrapper for SendOneRaw +func SendOne(format string, a ...interface{}) (reply []byte, ok bool, err error) { + request := fmt.Sprintf(format, a...) + return SendOneRaw([]byte(request)) +} + +// SendOneRaw uses a throw-away RPCOut to send a request +func SendOneRaw(request []byte) (reply []byte, ok bool, err error) { + out := &RPCOut{} + if err = out.Start(); err != nil { + return + } + if reply, ok, err = out.Send(request); err != nil { + return + } + if err = out.Stop(); err != nil { + return + } + return +} + +// RPCOut is an ougoing connection from the VM to the hypervisor +type RPCOut struct { + channel *message.Channel +} + +// Start opens the connection +func (out *RPCOut) Start() error { + channel, err := message.NewChannel(rpciProtocolNum) + if err != nil { + return err + } + out.channel = channel + return nil +} + +// Stop closes the connection +func (out *RPCOut) Stop() error { + err := out.channel.Close() + out.channel = nil + return err +} + +// Send emits a request and receives a response +func (out *RPCOut) Send(request []byte) (reply []byte, ok bool, err error) { + if err = out.channel.Send(request); err != nil { + return + } + + var resp []byte + if resp, err = out.channel.Receive(); err != nil { + return + } + + switch string(resp[:2]) { + case "0 ": + reply = resp[2:] + case "1 ": + reply = resp[2:] + ok = true + default: + err = ErrRpciFormat + } + return +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/rpcvmx/rpcvmx.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/rpcvmx/rpcvmx.go new file mode 100644 index 00000000..a832ad7f --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/rpcvmx/rpcvmx.go @@ -0,0 +1,101 @@ +// Copyright 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 rpcvmx + +import ( + "fmt" + "strconv" + "strings" + + "github.com/vmware/vmw-guestinfo/rpcout" +) + +const ( + prefix = "guestinfo" +) + +// Config gives access to the vmx config through the VMware backdoor +type Config struct{} + +// NewConfig creates a new Config object +func NewConfig() *Config { + return &Config{} +} + +// String returns the config string in the guestinfo.* namespace +func (c *Config) String(key string, defaultValue string) (string, error) { + // add "guestinfo." prefix if missing + if !strings.HasPrefix(key, prefix) { + key = fmt.Sprintf("%s.%s", prefix, key) + } + + out, ok, err := rpcout.SendOne("info-get %s", key) + if err != nil { + return "", err + } else if !ok { + return defaultValue, nil + } + return string(out), nil +} + +// Bool returns the config boolean in the guestinfo.* namespace +func (c *Config) Bool(key string, defaultValue bool) (bool, error) { + val, err := c.String(key, fmt.Sprintf("%t", defaultValue)) + if err != nil { + return false, err + } + res, err := strconv.ParseBool(val) + if err != nil { + return defaultValue, nil + } + return res, nil +} + +// Int returns the config integer in the guestinfo.* namespace +func (c *Config) Int(key string, defaultValue int) (int, error) { + val, err := c.String(key, "") + if err != nil { + return 0, err + } + res, err := strconv.Atoi(val) + if err != nil { + return defaultValue, nil + } + return res, nil +} + +// SetString sets the guestinfo.KEY with the string VALUE +func (c *Config) SetString(key string, value string) error { + // add "guestinfo." prefix if missing + if !strings.HasPrefix(key, prefix) { + key = fmt.Sprintf("%s.%s", prefix, key) + } + + _, _, err := rpcout.SendOne("info-set %s %s", key, value) + if err != nil { + return err + } + return nil +} + +// SetString sets the guestinfo.KEY with the bool VALUE +func (c *Config) SetBool(key string, value bool) error { + return c.SetString(key, strconv.FormatBool(value)) +} + +// SetString sets the guestinfo.KEY with the int VALUE +func (c *Config) SetInt(key string, value int) error { + return c.SetString(key, strconv.Itoa(value)) +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/util/util.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/util/util.go new file mode 100644 index 00000000..7445c0b3 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/util/util.go @@ -0,0 +1,58 @@ +// Copyright 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 util + +import ( + "reflect" + "runtime" + "testing" +) + +// Test utilities. + +func AssertEqual(t *testing.T, a interface{}, b interface{}) bool { + if !reflect.DeepEqual(a, b) { + Fail(t) + return false + } + + return true +} + +func AssertNoError(t *testing.T, err error) bool { + if err != nil { + t.Logf("error :%s", err.Error()) + Fail(t) + return false + } + + return true +} + +func AssertNotNil(t *testing.T, a interface{}) bool { + val := reflect.ValueOf(a) + if val.IsNil() { + Fail(t) + return false + } + + return true +} + +func Fail(t *testing.T) { + _, file, line, _ := runtime.Caller(2) + t.Logf("FAIL on %s:%d", file, line) + t.Fail() +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck.go new file mode 100644 index 00000000..c46cc5e4 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck.go @@ -0,0 +1,83 @@ +// Copyright 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 vmcheck + +import ( + "encoding/binary" + + "github.com/vmware/vmw-guestinfo/bdoor" +) + +// From https://github.com/intel-go/cpuid/blob/master/cpuidlow_amd64.s +// Get the CPU ID low level leaf values. +func cpuid_low(arg1, arg2 uint32) (eax, ebx, ecx, edx uint32) + +// IsVirtualWorld returns true if running in a VM and the backdoor is available. +func IsVirtualWorld() (bool, error) { + // Test the HV bit is set + if !IsVirtualCPU() { + return false, nil + } + + // Test if backdoor port is available. + if isVM, err := hypervisorPortCheck(); err != nil || !isVM { + return isVM, err + } + + return true, nil +} + +// hypervisorPortCheck tests the availability of the HV port. +func hypervisorPortCheck() (bool, error) { + // Privilege level 3 to access all ports above 0x3ff + if err := openPortsAccess(); err != nil { + return false, err + } + + p := &bdoor.BackdoorProto{} + + p.CX.AsUInt32().SetWord(bdoor.CommandGetVersion) + out := p.InOut() + // if there is no device, we get back all 1s + return (0xffffffff != out.AX.AsUInt32().Word()) && (0 != out.AX.AsUInt32().Word()), nil +} + +// IsVirtualCPU checks if the cpu is a virtual CPU running on ESX. It checks for +// the HV bit in the ECX register of the CPUID leaf 0x1. Intel and AMD CPUs +// reserve this bit to indicate if the CPU is running in a HV. See +// https://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits +// for details. If this bit is set, the reserved cpuid levels are used to pass +// information from the HV to the guest. In ESX, this is the repeating string +// "VMwareVMware". +func IsVirtualCPU() bool { + HV := uint32(1 << 31) + _, _, c, _ := cpuid_low(0x1, 0) + if (c & HV) != HV { + return false + } + + _, b, c, d := cpuid_low(0x40000000, 0) + + buf := make([]byte, 12) + binary.LittleEndian.PutUint32(buf, b) + binary.LittleEndian.PutUint32(buf[4:], c) + binary.LittleEndian.PutUint32(buf[8:], d) + + if string(buf) != "VMwareVMware" { + return false + } + + return true +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck_386.s b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck_386.s new file mode 100644 index 00000000..c029ece2 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck_386.s @@ -0,0 +1,13 @@ +#include "textflag.h" + +// From https://github.com/intel-go/cpuid/blob/master/cpuidlow_amd64.s +// func cpuid_low(arg1, arg2 uint32) (eax, ebx, ecx, edx uint32) +TEXT ·cpuid_low(SB), NOSPLIT, $0-24 + MOVL arg1+0(FP), AX + MOVL arg2+4(FP), CX + CPUID + MOVL AX, eax+8(FP) + MOVL BX, ebx+12(FP) + MOVL CX, ecx+16(FP) + MOVL DX, edx+20(FP) + RET diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck_amd64.s b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck_amd64.s new file mode 100644 index 00000000..c029ece2 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck_amd64.s @@ -0,0 +1,13 @@ +#include "textflag.h" + +// From https://github.com/intel-go/cpuid/blob/master/cpuidlow_amd64.s +// func cpuid_low(arg1, arg2 uint32) (eax, ebx, ecx, edx uint32) +TEXT ·cpuid_low(SB), NOSPLIT, $0-24 + MOVL arg1+0(FP), AX + MOVL arg2+4(FP), CX + CPUID + MOVL AX, eax+8(FP) + MOVL BX, ebx+12(FP) + MOVL CX, ecx+16(FP) + MOVL DX, edx+20(FP) + RET diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck_general.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck_general.go new file mode 100644 index 00000000..1e9217c9 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck_general.go @@ -0,0 +1,23 @@ +// Copyright 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. + +// +build !linux + +package vmcheck + +// probably not gonna work. Instead, implement a platform-specific variant, and +// add the platform to above build flags +func openPortsAccess() error { + return nil +} diff --git a/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck_linux.go b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck_linux.go new file mode 100644 index 00000000..46460f9a --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vendor/github.com/vmware/vmw-guestinfo/vmcheck/vmcheck_linux.go @@ -0,0 +1,22 @@ +// Copyright 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 vmcheck + +import "syscall" + +func openPortsAccess() error { + // Privilege level 3 to access all ports above 0x3ff + return syscall.Iopl(3) +} diff --git a/vendor/github.com/vmware/govmomi/view/container_view.go b/vendor/github.com/vmware/govmomi/view/container_view.go new file mode 100644 index 00000000..0e3268b8 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/view/container_view.go @@ -0,0 +1,130 @@ +/* +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 view + +import ( + "context" + + "github.com/vmware/govmomi/property" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" +) + +type ContainerView struct { + ManagedObjectView +} + +func NewContainerView(c *vim25.Client, ref types.ManagedObjectReference) *ContainerView { + return &ContainerView{ + ManagedObjectView: *NewManagedObjectView(c, ref), + } +} + +// Retrieve populates dst as property.Collector.Retrieve does, for all entities in the view of types specified by kind. +func (v ContainerView) Retrieve(ctx context.Context, kind []string, ps []string, dst interface{}) error { + pc := property.DefaultCollector(v.Client()) + + ospec := types.ObjectSpec{ + Obj: v.Reference(), + Skip: types.NewBool(true), + SelectSet: []types.BaseSelectionSpec{ + &types.TraversalSpec{ + Type: v.Reference().Type, + Path: "view", + }, + }, + } + + var pspec []types.PropertySpec + + if len(kind) == 0 { + kind = []string{"ManagedEntity"} + } + + for _, t := range kind { + spec := types.PropertySpec{ + Type: t, + } + + if len(ps) == 0 { + spec.All = types.NewBool(true) + } else { + spec.PathSet = ps + } + + pspec = append(pspec, spec) + } + + req := types.RetrieveProperties{ + SpecSet: []types.PropertyFilterSpec{ + { + ObjectSet: []types.ObjectSpec{ospec}, + PropSet: pspec, + }, + }, + } + + res, err := pc.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 (v ContainerView) RetrieveWithFilter(ctx context.Context, kind []string, ps []string, dst interface{}, filter property.Filter) error { + if len(filter) == 0 { + return v.Retrieve(ctx, kind, ps, dst) + } + + var content []types.ObjectContent + + err := v.Retrieve(ctx, kind, filter.Keys(), &content) + if err != nil { + return err + } + + objs := filter.MatchObjectContent(content) + + pc := property.DefaultCollector(v.Client()) + + return pc.Retrieve(ctx, objs, ps, dst) +} + +// Find returns object references for entities of type kind, matching the given filter. +func (v ContainerView) Find(ctx context.Context, kind []string, filter property.Filter) ([]types.ManagedObjectReference, error) { + if len(filter) == 0 { + // Ensure we have at least 1 filter to avoid retrieving all properties. + filter = property.Filter{"name": "*"} + } + + var content []types.ObjectContent + + err := v.Retrieve(ctx, kind, filter.Keys(), &content) + if err != nil { + return nil, err + } + + return filter.MatchObjectContent(content), nil +} diff --git a/vendor/github.com/vmware/govmomi/view/list_view.go b/vendor/github.com/vmware/govmomi/view/list_view.go index 145d4dc5..e8cfb504 100644 --- a/vendor/github.com/vmware/govmomi/view/list_view.go +++ b/vendor/github.com/vmware/govmomi/view/list_view.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2015 VMware, Inc. All Rights Reserved. +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. @@ -17,17 +17,46 @@ limitations under the License. package view import ( - "github.com/vmware/govmomi/object" + "context" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/types" ) type ListView struct { - *object.ListView + ManagedObjectView } func NewListView(c *vim25.Client, ref types.ManagedObjectReference) *ListView { return &ListView{ - ListView: object.NewListView(c, ref), + ManagedObjectView: *NewManagedObjectView(c, ref), + } +} + +func (v ListView) Add(ctx context.Context, refs []types.ManagedObjectReference) error { + req := types.ModifyListView{ + This: v.Reference(), + Add: refs, + } + _, err := methods.ModifyListView(ctx, v.Client(), &req) + return err +} + +func (v ListView) Remove(ctx context.Context, refs []types.ManagedObjectReference) error { + req := types.ModifyListView{ + This: v.Reference(), + Remove: refs, + } + _, err := methods.ModifyListView(ctx, v.Client(), &req) + return err +} + +func (v ListView) Reset(ctx context.Context, refs []types.ManagedObjectReference) error { + req := types.ResetListView{ + This: v.Reference(), + Obj: refs, } + _, err := methods.ResetListView(ctx, v.Client(), &req) + return err } diff --git a/vendor/github.com/vmware/govmomi/view/managed_object_view.go b/vendor/github.com/vmware/govmomi/view/managed_object_view.go new file mode 100644 index 00000000..805c8643 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/view/managed_object_view.go @@ -0,0 +1,52 @@ +/* +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 view + +import ( + "context" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/types" +) + +type ManagedObjectView struct { + object.Common +} + +func NewManagedObjectView(c *vim25.Client, ref types.ManagedObjectReference) *ManagedObjectView { + return &ManagedObjectView{ + Common: object.NewCommon(c, ref), + } +} + +func (v *ManagedObjectView) TraversalSpec() *types.TraversalSpec { + return &types.TraversalSpec{ + Path: "view", + Type: v.Reference().Type, + } +} + +func (v *ManagedObjectView) Destroy(ctx context.Context) error { + req := types.DestroyView{ + This: v.Reference(), + } + + _, err := methods.DestroyView(ctx, v.Client(), &req) + return err +} diff --git a/vendor/github.com/vmware/govmomi/view/manager.go b/vendor/github.com/vmware/govmomi/view/manager.go index 7a227eaa..d44def0c 100644 --- a/vendor/github.com/vmware/govmomi/view/manager.go +++ b/vendor/github.com/vmware/govmomi/view/manager.go @@ -50,3 +50,20 @@ func (m Manager) CreateListView(ctx context.Context, objects []types.ManagedObje return NewListView(m.Client(), res.Returnval), nil } + +func (m Manager) CreateContainerView(ctx context.Context, container types.ManagedObjectReference, managedObjectTypes []string, recursive bool) (*ContainerView, error) { + + req := types.CreateContainerView{ + This: m.Common.Reference(), + Container: container, + Recursive: recursive, + Type: managedObjectTypes, + } + + res, err := methods.CreateContainerView(ctx, m.Client(), &req) + if err != nil { + return nil, err + } + + return NewContainerView(m.Client(), res.Returnval), nil +} diff --git a/vendor/github.com/vmware/govmomi/view/task_view.go b/vendor/github.com/vmware/govmomi/view/task_view.go new file mode 100644 index 00000000..68f62f8d --- /dev/null +++ b/vendor/github.com/vmware/govmomi/view/task_view.go @@ -0,0 +1,125 @@ +/* +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 view + +import ( + "context" + + "github.com/vmware/govmomi/property" + "github.com/vmware/govmomi/vim25/types" +) + +// TaskView extends ListView such that it can follow a ManagedEntity's recentTask updates. +type TaskView struct { + *ListView + + Follow bool + + Watch *types.ManagedObjectReference +} + +// CreateTaskView creates a new ListView that optionally watches for a ManagedEntity's recentTask updates. +func (m Manager) CreateTaskView(ctx context.Context, watch *types.ManagedObjectReference) (*TaskView, error) { + l, err := m.CreateListView(ctx, nil) + if err != nil { + return nil, err + } + + tv := &TaskView{ + ListView: l, + Watch: watch, + } + + return tv, nil +} + +// Collect calls function f for each Task update. +func (v TaskView) Collect(ctx context.Context, f func([]types.TaskInfo)) error { + // Using TaskHistoryCollector would be less clunky, but it isn't supported on ESX at all. + ref := v.Reference() + filter := new(property.WaitFilter).Add(ref, "Task", []string{"info"}, v.TraversalSpec()) + + if v.Watch != nil { + filter.Add(*v.Watch, v.Watch.Type, []string{"recentTask"}) + } + + pc := property.DefaultCollector(v.Client()) + + completed := make(map[string]bool) + + return property.WaitForUpdates(ctx, pc, filter, func(updates []types.ObjectUpdate) bool { + var infos []types.TaskInfo + var prune []types.ManagedObjectReference + var tasks []types.ManagedObjectReference + var reset func() + + for _, update := range updates { + for _, change := range update.ChangeSet { + if change.Name == "recentTask" { + tasks = change.Val.(types.ArrayOfManagedObjectReference).ManagedObjectReference + if len(tasks) != 0 { + reset = func() { + _ = v.Reset(ctx, tasks) + + // Remember any tasks we've reported as complete already, + // to avoid reporting multiple times when Reset is triggered. + rtasks := make(map[string]bool) + for i := range tasks { + if _, ok := completed[tasks[i].Value]; ok { + rtasks[tasks[i].Value] = true + } + } + completed = rtasks + } + } + + continue + } + + info, ok := change.Val.(types.TaskInfo) + if !ok { + continue + } + + if !completed[info.Task.Value] { + infos = append(infos, info) + } + + if v.Follow && info.CompleteTime != nil { + prune = append(prune, info.Task) + completed[info.Task.Value] = true + } + } + } + + if len(infos) != 0 { + f(infos) + } + + if reset != nil { + reset() + } else if len(prune) != 0 { + _ = v.Remove(ctx, prune) + } + + if len(tasks) != 0 && len(infos) == 0 { + return false + } + + return !v.Follow + }) +} diff --git a/vendor/github.com/vmware/govmomi/vim25/methods/methods.go b/vendor/github.com/vmware/govmomi/vim25/methods/methods.go index 79c49435..0895a81c 100644 --- a/vendor/github.com/vmware/govmomi/vim25/methods/methods.go +++ b/vendor/github.com/vmware/govmomi/vim25/methods/methods.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/vim25/mo/ancestors.go b/vendor/github.com/vmware/govmomi/vim25/mo/ancestors.go index 86a3a69f..d3da5b18 100644 --- a/vendor/github.com/vmware/govmomi/vim25/mo/ancestors.go +++ b/vendor/github.com/vmware/govmomi/vim25/mo/ancestors.go @@ -104,6 +104,8 @@ func Ancestors(ctx context.Context, rt soap.RoundTripper, pc, obj types.ManagedO 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())) diff --git a/vendor/github.com/vmware/govmomi/vim25/mo/mo.go b/vendor/github.com/vmware/govmomi/vim25/mo/mo.go index 0764eb49..ede6e44d 100644 --- a/vendor/github.com/vmware/govmomi/vim25/mo/mo.go +++ b/vendor/github.com/vmware/govmomi/vim25/mo/mo.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved. +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. @@ -150,6 +150,30 @@ func init() { t["ContainerView"] = reflect.TypeOf((*ContainerView)(nil)).Elem() } +type CryptoManager struct { + Self types.ManagedObjectReference + + Enabled bool `mo:"enabled"` +} + +func (m CryptoManager) Reference() types.ManagedObjectReference { + return m.Self +} + +func init() { + t["CryptoManager"] = reflect.TypeOf((*CryptoManager)(nil)).Elem() +} + +type CryptoManagerKmip struct { + CryptoManager + + KmipServers []types.KmipClusterInfo `mo:"kmipServers"` +} + +func init() { + t["CryptoManagerKmip"] = reflect.TypeOf((*CryptoManagerKmip)(nil)).Elem() +} + type CustomFieldsManager struct { Self types.ManagedObjectReference @@ -356,6 +380,34 @@ func init() { t["ExtensionManager"] = reflect.TypeOf((*ExtensionManager)(nil)).Elem() } +type FailoverClusterConfigurator struct { + Self types.ManagedObjectReference + + DisabledConfigureMethod []string `mo:"disabledConfigureMethod"` +} + +func (m FailoverClusterConfigurator) Reference() types.ManagedObjectReference { + return m.Self +} + +func init() { + t["FailoverClusterConfigurator"] = reflect.TypeOf((*FailoverClusterConfigurator)(nil)).Elem() +} + +type FailoverClusterManager struct { + Self types.ManagedObjectReference + + DisabledClusterMethod []string `mo:"disabledClusterMethod"` +} + +func (m FailoverClusterManager) Reference() types.ManagedObjectReference { + return m.Self +} + +func init() { + t["FailoverClusterManager"] = reflect.TypeOf((*FailoverClusterManager)(nil)).Elem() +} + type FileManager struct { Self types.ManagedObjectReference } @@ -461,6 +513,18 @@ func init() { t["GuestWindowsRegistryManager"] = reflect.TypeOf((*GuestWindowsRegistryManager)(nil)).Elem() } +type HealthUpdateManager struct { + Self types.ManagedObjectReference +} + +func (m HealthUpdateManager) Reference() types.ManagedObjectReference { + return m.Self +} + +func init() { + t["HealthUpdateManager"] = reflect.TypeOf((*HealthUpdateManager)(nil)).Elem() +} + type HistoryCollector struct { Self types.ManagedObjectReference @@ -695,8 +759,9 @@ func init() { type HostGraphicsManager struct { ExtensibleManagedObject - GraphicsInfo []types.HostGraphicsInfo `mo:"graphicsInfo"` - SharedPassthruGpuTypes []string `mo:"sharedPassthruGpuTypes"` + GraphicsInfo []types.HostGraphicsInfo `mo:"graphicsInfo"` + GraphicsConfig *types.HostGraphicsConfig `mo:"graphicsConfig"` + SharedPassthruGpuTypes []string `mo:"sharedPassthruGpuTypes"` } func init() { @@ -803,7 +868,8 @@ func init() { type HostPciPassthruSystem struct { ExtensibleManagedObject - PciPassthruInfo []types.BaseHostPciPassthruInfo `mo:"pciPassthruInfo"` + PciPassthruInfo []types.BaseHostPciPassthruInfo `mo:"pciPassthruInfo"` + SriovDevicePoolInfo []types.BaseHostSriovDevicePoolInfo `mo:"sriovDevicePoolInfo"` } func init() { @@ -868,6 +934,18 @@ func init() { t["HostSnmpSystem"] = reflect.TypeOf((*HostSnmpSystem)(nil)).Elem() } +type HostSpecificationManager struct { + Self types.ManagedObjectReference +} + +func (m HostSpecificationManager) Reference() types.ManagedObjectReference { + return m.Self +} + +func init() { + t["HostSpecificationManager"] = reflect.TypeOf((*HostSpecificationManager)(nil)).Elem() +} + type HostStorageSystem struct { ExtensibleManagedObject @@ -931,6 +1009,14 @@ func init() { t["HostVMotionSystem"] = reflect.TypeOf((*HostVMotionSystem)(nil)).Elem() } +type HostVStorageObjectManager struct { + VStorageObjectManagerBase +} + +func init() { + t["HostVStorageObjectManager"] = reflect.TypeOf((*HostVStorageObjectManager)(nil)).Elem() +} + type HostVirtualNicManager struct { ExtensibleManagedObject @@ -984,18 +1070,6 @@ func init() { t["HttpNfcLease"] = reflect.TypeOf((*HttpNfcLease)(nil)).Elem() } -type InternalDynamicTypeManager struct { - Self types.ManagedObjectReference -} - -func (m InternalDynamicTypeManager) Reference() types.ManagedObjectReference { - return m.Self -} - -func init() { - t["InternalDynamicTypeManager"] = reflect.TypeOf((*InternalDynamicTypeManager)(nil)).Elem() -} - type InventoryView struct { ManagedObjectView } @@ -1163,6 +1237,9 @@ func init() { type OpaqueNetwork struct { Network + + Capability *types.OpaqueNetworkCapability `mo:"capability"` + ExtraConfig []types.BaseOptionValue `mo:"extraConfig"` } func init() { @@ -1302,18 +1379,6 @@ func init() { t["PropertyFilter"] = reflect.TypeOf((*PropertyFilter)(nil)).Elem() } -type ReflectManagedMethodExecuter struct { - Self types.ManagedObjectReference -} - -func (m ReflectManagedMethodExecuter) Reference() types.ManagedObjectReference { - return m.Self -} - -func init() { - t["ReflectManagedMethodExecuter"] = reflect.TypeOf((*ReflectManagedMethodExecuter)(nil)).Elem() -} - type ResourcePlanningManager struct { Self types.ManagedObjectReference } @@ -1520,6 +1585,26 @@ func init() { t["UserDirectory"] = reflect.TypeOf((*UserDirectory)(nil)).Elem() } +type VStorageObjectManagerBase struct { + Self types.ManagedObjectReference +} + +func (m VStorageObjectManagerBase) Reference() types.ManagedObjectReference { + return m.Self +} + +func init() { + t["VStorageObjectManagerBase"] = reflect.TypeOf((*VStorageObjectManagerBase)(nil)).Elem() +} + +type VcenterVStorageObjectManager struct { + VStorageObjectManagerBase +} + +func init() { + t["VcenterVStorageObjectManager"] = reflect.TypeOf((*VcenterVStorageObjectManager)(nil)).Elem() +} + type View struct { Self types.ManagedObjectReference } diff --git a/vendor/github.com/vmware/govmomi/vim25/soap/client.go b/vendor/github.com/vmware/govmomi/vim25/soap/client.go index e89ffc81..8747e07b 100644 --- a/vendor/github.com/vmware/govmomi/vim25/soap/client.go +++ b/vendor/github.com/vmware/govmomi/vim25/soap/client.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved. +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. @@ -58,6 +58,10 @@ const ( DefaultMinVimVersion = "5.5" ) +type header struct { + Cookie string `xml:"vcSessionCookie,omitempty"` +} + type Client struct { http.Client @@ -73,6 +77,8 @@ type Client struct { Namespace string // Vim namespace Version string // Vim version UserAgent string + + header *header } var schemeMatch = regexp.MustCompile(`^\w+://`) @@ -147,6 +153,32 @@ func NewClient(u *url.URL, insecure bool) *Client { return &c } +// NewServiceClient creates a NewClient with the given URL.Path and namespace. +func (c *Client) NewServiceClient(path string, namespace string) *Client { + u := c.URL() + u.Path = path + + client := NewClient(u, c.k) + + client.Namespace = namespace + + // Copy the cookies + client.Client.Jar.SetCookies(u, c.Client.Jar.Cookies(u)) + + // Set SOAP Header cookie + for _, cookie := range client.Jar.Cookies(u) { + if cookie.Name == "vmware_soap_session" { + client.header = &header{ + Cookie: cookie.Value, + } + + break + } + } + + return client +} + // SetRootCAs defines the set of root certificate authorities // that clients use when verifying server certificates. // By default TLS uses the host's root CA set. @@ -401,6 +433,8 @@ func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error reqEnv := Envelope{Body: reqBody} resEnv := Envelope{Body: resBody} + reqEnv.Header = c.header + // Create debugging context for this round trip d := c.d.newRoundTrip() if d.enabled() { @@ -621,33 +655,24 @@ func (c *Client) Download(u *url.URL, param *Download) (io.ReadCloser, int64, er return nil, 0, err } - return res.Body, res.ContentLength, nil + r := res.Body + + return r, res.ContentLength, nil } -// DownloadFile GETs the given URL to a local file -func (c *Client) DownloadFile(file string, u *url.URL, param *Download) error { +func (c *Client) WriteFile(file string, src io.Reader, size int64, s progress.Sinker) error { var err error - if param == nil { - param = &DefaultDownload - } - rc, contentLength, err := c.Download(u, param) - if err != nil { - return err - } - defer rc.Close() - - var r io.Reader = rc + r := src fh, err := os.Create(file) if err != nil { return err } - defer fh.Close() - if param.Progress != nil { - pr := progress.NewReader(param.Progress, r, contentLength) - r = pr + if s != nil { + pr := progress.NewReader(s, src, size) + src = pr // Mark progress reader as done when returning from this function. defer func() { @@ -656,16 +681,27 @@ func (c *Client) DownloadFile(file string, u *url.URL, param *Download) error { } _, err = io.Copy(fh, r) - if err != nil { - return err + + cerr := fh.Close() + + if err == nil { + err = cerr } - // Assign error before returning so that it gets picked up by the deferred - // function marking the progress reader as done. - err = fh.Close() + return err +} + +// DownloadFile GETs the given URL to a local file +func (c *Client) DownloadFile(file string, u *url.URL, param *Download) error { + var err error + if param == nil { + param = &DefaultDownload + } + + rc, contentLength, err := c.Download(u, param) if err != nil { return err } - return nil + return c.WriteFile(file, rc, contentLength, param.Progress) } diff --git a/vendor/github.com/vmware/govmomi/vim25/soap/error.go b/vendor/github.com/vmware/govmomi/vim25/soap/error.go index 0408e7bf..d8920852 100644 --- a/vendor/github.com/vmware/govmomi/vim25/soap/error.go +++ b/vendor/github.com/vmware/govmomi/vim25/soap/error.go @@ -36,7 +36,13 @@ type soapFaultError struct { } func (s soapFaultError) Error() string { - return fmt.Sprintf("%s: %s", s.fault.Code, s.fault.String) + msg := s.fault.String + + if msg == "" { + msg = reflect.TypeOf(s.fault.Detail.Fault).Name() + } + + return fmt.Sprintf("%s: %s", s.fault.Code, msg) } type vimFaultError struct { diff --git a/vendor/github.com/vmware/govmomi/vim25/soap/soap.go b/vendor/github.com/vmware/govmomi/vim25/soap/soap.go index ea35e77a..a8baa012 100644 --- a/vendor/github.com/vmware/govmomi/vim25/soap/soap.go +++ b/vendor/github.com/vmware/govmomi/vim25/soap/soap.go @@ -22,15 +22,11 @@ import ( ) type Envelope struct { - XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` - Header *Header `xml:",omitempty"` + XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` + Header interface{} `xml:",omitempty"` Body interface{} } -type Header struct { - XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Header"` -} - type Fault struct { XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"` Code string `xml:"faultcode"` diff --git a/vendor/github.com/vmware/govmomi/vim25/types/enum.go b/vendor/github.com/vmware/govmomi/vim25/types/enum.go index 1db935ef..02f7f3cb 100644 --- a/vendor/github.com/vmware/govmomi/vim25/types/enum.go +++ b/vendor/github.com/vmware/govmomi/vim25/types/enum.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/vim25/types/helpers.go b/vendor/github.com/vmware/govmomi/vim25/types/helpers.go index 7b72358b..2364ed42 100644 --- a/vendor/github.com/vmware/govmomi/vim25/types/helpers.go +++ b/vendor/github.com/vmware/govmomi/vim25/types/helpers.go @@ -46,3 +46,7 @@ func (r *ManagedObjectReference) FromString(o string) bool { return true } + +func (c *PerfCounterInfo) Name() string { + return c.GroupInfo.GetElementDescription().Key + "." + c.NameInfo.GetElementDescription().Key + "." + string(c.RollupType) +} diff --git a/vendor/github.com/vmware/govmomi/vim25/types/if.go b/vendor/github.com/vmware/govmomi/vim25/types/if.go index 5ae275c6..dbf594cf 100644 --- a/vendor/github.com/vmware/govmomi/vim25/types/if.go +++ b/vendor/github.com/vmware/govmomi/vim25/types/if.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved. +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. diff --git a/vendor/github.com/vmware/govmomi/vim25/types/registry.go b/vendor/github.com/vmware/govmomi/vim25/types/registry.go index 8f238088..ff7c302d 100644 --- a/vendor/github.com/vmware/govmomi/vim25/types/registry.go +++ b/vendor/github.com/vmware/govmomi/vim25/types/registry.go @@ -16,15 +16,28 @@ limitations under the License. package types -import "reflect" +import ( + "reflect" + "strings" +) var t = map[string]reflect.Type{} +func Add(name string, kind reflect.Type) { + t[name] = kind +} + type Func func(string) (reflect.Type, bool) func TypeFunc() Func { return func(name string) (reflect.Type, bool) { typ, ok := t[name] + if !ok { + // The /sdk endpoint does not prefix types with the namespace, + // but extension endpoints, such as /pbm/sdk do. + name = strings.TrimPrefix(name, "vim25:") + typ, ok = t[name] + } return typ, ok } } diff --git a/vendor/github.com/vmware/govmomi/vim25/types/types.go b/vendor/github.com/vmware/govmomi/vim25/types/types.go index ffb4606c..cb703582 100644 --- a/vendor/github.com/vmware/govmomi/vim25/types/types.go +++ b/vendor/github.com/vmware/govmomi/vim25/types/types.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved. +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. @@ -18341,9 +18341,9 @@ func init() { type GuestPosixFileAttributes struct { GuestFileAttributes - OwnerId int32 `xml:"ownerId,omitempty"` - GroupId int32 `xml:"groupId,omitempty"` - Permissions int64 `xml:"permissions,omitempty"` + OwnerId *int32 `xml:"ownerId"` + GroupId *int32 `xml:"groupId"` + Permissions int64 `xml:"permissions,omitempty"` } func init() { @@ -28546,7 +28546,7 @@ func init() { type MethodActionArgument struct { DynamicData - Value AnyType `xml:"value,omitempty,typeattr"` + Value AnyType `xml:"value,typeattr"` } func init() { @@ -31074,7 +31074,7 @@ type OptionValue struct { DynamicData Key string `xml:"key"` - Value AnyType `xml:"value,omitempty,typeattr"` + Value AnyType `xml:"value,typeattr"` } func init() { @@ -53154,8 +53154,8 @@ type WaitForUpdatesResponse struct { type WaitOptions struct { DynamicData - MaxWaitSeconds int32 `xml:"maxWaitSeconds,omitempty"` - MaxObjectUpdates int32 `xml:"maxObjectUpdates,omitempty"` + MaxWaitSeconds *int32 `xml:"maxWaitSeconds"` + MaxObjectUpdates int32 `xml:"maxObjectUpdates,omitempty"` } func init() { diff --git a/vendor/github.com/vmware/govmomi/vim25/xml/extras.go b/vendor/github.com/vmware/govmomi/vim25/xml/extras.go index 3b7f44b0..9a15b7c8 100644 --- a/vendor/github.com/vmware/govmomi/vim25/xml/extras.go +++ b/vendor/github.com/vmware/govmomi/vim25/xml/extras.go @@ -23,6 +23,8 @@ import ( var xmlSchemaInstance = Name{Space: "http://www.w3.org/2001/XMLSchema-instance", Local: "type"} +var xsiType = Name{Space: "xsi", Local: "type"} + var stringToTypeMap = map[string]reflect.Type{ "xsd:boolean": reflect.TypeOf((*bool)(nil)).Elem(), "xsd:byte": reflect.TypeOf((*int8)(nil)).Elem(), diff --git a/vendor/github.com/vmware/govmomi/vim25/xml/read.go b/vendor/github.com/vmware/govmomi/vim25/xml/read.go index 5b407b79..fe35fce6 100644 --- a/vendor/github.com/vmware/govmomi/vim25/xml/read.go +++ b/vendor/github.com/vmware/govmomi/vim25/xml/read.go @@ -272,13 +272,15 @@ var ( func (p *Decoder) typeForElement(val reflect.Value, start *StartElement) reflect.Type { t := "" for i, a := range start.Attr { - if a.Name == xmlSchemaInstance { + if a.Name == xmlSchemaInstance || a.Name == xsiType { t = a.Value // HACK: ensure xsi:type is last in the list to avoid using that value for // a "type" attribute, such as ManagedObjectReference.Type for example. // Note that xsi:type is already the last attribute in VC/ESX responses. // This is only an issue with govmomi simulator generated responses. // Proper fix will require finding a few needles in this xml package haystack. + // Note: govmomi uses xmlSchemaInstance, other clients (e.g. rbvmomi) use xsiType. + // They are the same thing to XML parsers, but not to this hack here. x := len(start.Attr) - 1 if i != x { start.Attr[i] = start.Attr[x] diff --git a/vendor/github.com/vmware/govmomi/vmdk/import.go b/vendor/github.com/vmware/govmomi/vmdk/import.go new file mode 100644 index 00000000..64301f0d --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vmdk/import.go @@ -0,0 +1,335 @@ +/* +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. +nSee the License for the specific language governing permissions and +limitations under the License. +*/ + +package vmdk + +import ( + "bytes" + "context" + "encoding/binary" + "errors" + "fmt" + "io" + "os" + "path" + "path/filepath" + "strings" + "text/template" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/ovf" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/progress" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +var ( + ErrInvalidFormat = errors.New("vmdk: invalid format (must be streamOptimized)") +) + +// info is used to inspect a vmdk and generate an ovf template +type info struct { + Header struct { + MagicNumber uint32 + Version uint32 + Flags uint32 + Capacity uint64 + } + + Capacity uint64 + Size int64 + Name string + ImportName string +} + +// stat looks at the vmdk header to make sure the format is streamOptimized and +// extracts the disk capacity required to properly generate the ovf descriptor. +func stat(name string) (*info, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + + var di info + + var buf bytes.Buffer + + _, err = io.CopyN(&buf, f, int64(binary.Size(di.Header))) + + fi, _ := f.Stat() + + _ = f.Close() + + if err != nil { + return nil, err + } + + err = binary.Read(&buf, binary.LittleEndian, &di.Header) + if err != nil { + return nil, err + } + + if di.Header.MagicNumber != 0x564d444b { // SPARSE_MAGICNUMBER + return nil, ErrInvalidFormat + } + + if di.Header.Flags&(1<<16) == 0 { // SPARSEFLAG_COMPRESSED + // Needs to be converted, for example: + // vmware-vdiskmanager -r src.vmdk -t 5 dst.vmdk + // qemu-img convert -O vmdk -o subformat=streamOptimized src.vmdk dst.vmdk + return nil, ErrInvalidFormat + } + + di.Capacity = di.Header.Capacity * 512 // VMDK_SECTOR_SIZE + di.Size = fi.Size() + di.Name = filepath.Base(name) + di.ImportName = strings.TrimSuffix(di.Name, ".vmdk") + + return &di, nil +} + +// ovfenv is the minimal descriptor template required to import a vmdk +var ovfenv = ` + + + + + + Virtual disk information + + + + A virtual machine + {{ .ImportName }} + + The kind of installed guest operating system + + + Virtual hardware requirements + + Virtual Hardware Family + 0 + {{ .ImportName }} + vmx-07 + + + hertz * 10^6 + Number of Virtual CPUs + 1 virtual CPU(s) + 1 + 3 + 1 + + + byte * 2^20 + Memory Size + 1024MB of memory + 2 + 4 + 1024 + + + 0 + SCSI Controller + SCSI Controller 0 + 3 + VirtualSCSI + 6 + + + 0 + Hard Disk 1 + ovf:/disk/vmdisk1 + 9 + 3 + 17 + + + + +` + +// ovf returns an expanded descriptor template +func (di *info) ovf() (string, error) { + var buf bytes.Buffer + + tmpl, err := template.New("ovf").Parse(ovfenv) + if err != nil { + return "", err + } + + err = tmpl.Execute(&buf, di) + if err != nil { + return "", err + } + + return buf.String(), nil +} + +// ImportParams contains the set of optional params to the Import function. +// Note that "optional" may depend on environment, such as ESX or vCenter. +type ImportParams struct { + Path string + Logger progress.Sinker + Type types.VirtualDiskType + Force bool + Datacenter *object.Datacenter + Pool *object.ResourcePool + Folder *object.Folder + Host *object.HostSystem +} + +// Import uploads a local vmdk file specified by name to the given datastore. +func Import(ctx context.Context, c *vim25.Client, name string, datastore *object.Datastore, p ImportParams) error { + m := ovf.NewManager(c) + fm := datastore.NewFileManager(p.Datacenter, p.Force) + + disk, err := stat(name) + if err != nil { + return err + } + + var rename string + + p.Path = strings.TrimSuffix(p.Path, "/") + if p.Path != "" { + disk.ImportName = p.Path + rename = path.Join(disk.ImportName, disk.Name) + } + + // "target" is the path that will be created by ImportVApp() + // ImportVApp uses the same name for the VM and the disk. + target := fmt.Sprintf("%s/%s.vmdk", disk.ImportName, disk.ImportName) + + if _, err = datastore.Stat(ctx, target); err == nil { + if p.Force { + // If we don't delete, the nfc upload adds a file name suffix + if err = fm.Delete(ctx, target); err != nil { + return err + } + } else { + return fmt.Errorf("%s: %s", os.ErrExist, datastore.Path(target)) + } + } + + // If we need to rename at the end, check if the file exists early unless Force. + if !p.Force && rename != "" { + if _, err = datastore.Stat(ctx, rename); err == nil { + return fmt.Errorf("%s: %s", os.ErrExist, datastore.Path(rename)) + } + } + + // Expand the ovf template + descriptor, err := disk.ovf() + if err != nil { + return err + } + + pool := p.Pool // TODO: use datastore to derive a default + folder := p.Folder // TODO: use datacenter to derive a default + + kind := p.Type + if kind == "" { + kind = types.VirtualDiskTypeThin + } + + params := types.OvfCreateImportSpecParams{ + DiskProvisioning: string(kind), + EntityName: disk.ImportName, + } + + spec, err := m.CreateImportSpec(ctx, descriptor, pool, datastore, params) + if err != nil { + return err + } + if spec.Error != nil { + return errors.New(spec.Error[0].LocalizedMessage) + } + + lease, err := pool.ImportVApp(ctx, spec.ImportSpec, folder, p.Host) + if err != nil { + return err + } + + info, err := lease.Wait(ctx, spec.FileItem) + if err != nil { + return err + } + + f, err := os.Open(name) + if err != nil { + return err + } + + opts := soap.Upload{ + ContentLength: disk.Size, + Progress: p.Logger, + } + + u := lease.StartUpdater(ctx, info) + defer u.Done() + + item := info.Items[0] // we only have 1 disk to upload + + err = lease.Upload(ctx, item, f, opts) + + _ = f.Close() + + if err != nil { + return err + } + + if err = lease.Complete(ctx); err != nil { + return err + } + + // ImportVApp created a VM, here we detach the vmdk, then delete the VM. + vm := object.NewVirtualMachine(c, info.Entity) + + device, err := vm.Device(ctx) + if err != nil { + return err + } + + device = device.SelectByType((*types.VirtualDisk)(nil)) + + err = vm.RemoveDevice(ctx, true, device...) + if err != nil { + return err + } + + task, err := vm.Destroy(ctx) + if err != nil { + return err + } + + if err = task.Wait(ctx); err != nil { + return err + } + + if rename == "" { + return nil + } + + return fm.Move(ctx, target, rename) +} diff --git a/vendor/golang.org/x/crypto/ssh/agent/client.go b/vendor/golang.org/x/crypto/ssh/agent/client.go index ecfd7c58..dce7682f 100644 --- a/vendor/golang.org/x/crypto/ssh/agent/client.go +++ b/vendor/golang.org/x/crypto/ssh/agent/client.go @@ -57,6 +57,17 @@ type Agent interface { Signers() ([]ssh.Signer, error) } +// ConstraintExtension describes an optional constraint defined by users. +type ConstraintExtension struct { + // ExtensionName consist of a UTF-8 string suffixed by the + // implementation domain following the naming scheme defined + // in Section 4.2 of [RFC4251], e.g. "foo@example.com". + ExtensionName string + // ExtensionDetails contains the actual content of the extended + // constraint. + ExtensionDetails []byte +} + // AddedKey describes an SSH key to be added to an Agent. type AddedKey struct { // PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or @@ -73,6 +84,9 @@ type AddedKey struct { // ConfirmBeforeUse, if true, requests that the agent confirm with the // user before each use of this key. ConfirmBeforeUse bool + // ConstraintExtensions are the experimental or private-use constraints + // defined by users. + ConstraintExtensions []ConstraintExtension } // See [PROTOCOL.agent], section 3. @@ -94,8 +108,9 @@ const ( agentAddSmartcardKeyConstrained = 26 // 3.7 Key constraint identifiers - agentConstrainLifetime = 1 - agentConstrainConfirm = 2 + agentConstrainLifetime = 1 + agentConstrainConfirm = 2 + agentConstrainExtension = 3 ) // maxAgentResponseBytes is the maximum agent reply size that is accepted. This @@ -151,6 +166,19 @@ type publicKey struct { Rest []byte `ssh:"rest"` } +// 3.7 Key constraint identifiers +type constrainLifetimeAgentMsg struct { + LifetimeSecs uint32 `sshtype:"1"` +} + +type constrainExtensionAgentMsg struct { + ExtensionName string `sshtype:"3"` + ExtensionDetails []byte + + // Rest is a field used for parsing, not part of message + Rest []byte `ssh:"rest"` +} + // Key represents a protocol 2 public key as defined in // [PROTOCOL.agent], section 2.5.2. type Key struct { @@ -542,11 +570,7 @@ func (c *client) Add(key AddedKey) error { var constraints []byte if secs := key.LifetimeSecs; secs != 0 { - constraints = append(constraints, agentConstrainLifetime) - - var secsBytes [4]byte - binary.BigEndian.PutUint32(secsBytes[:], secs) - constraints = append(constraints, secsBytes[:]...) + constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...) } if key.ConfirmBeforeUse { diff --git a/vendor/golang.org/x/crypto/ssh/agent/server.go b/vendor/golang.org/x/crypto/ssh/agent/server.go index 68a333fa..321e48a2 100644 --- a/vendor/golang.org/x/crypto/ssh/agent/server.go +++ b/vendor/golang.org/x/crypto/ssh/agent/server.go @@ -106,7 +106,7 @@ func (s *server) processRequest(data []byte) (interface{}, error) { return nil, s.agent.Lock(req.Passphrase) case agentUnlock: - var req agentLockMsg + var req agentUnlockMsg if err := ssh.Unmarshal(data, &req); err != nil { return nil, err } @@ -155,6 +155,44 @@ func (s *server) processRequest(data []byte) (interface{}, error) { return nil, fmt.Errorf("unknown opcode %d", data[0]) } +func parseConstraints(constraints []byte) (lifetimeSecs uint32, confirmBeforeUse bool, extensions []ConstraintExtension, err error) { + for len(constraints) != 0 { + switch constraints[0] { + case agentConstrainLifetime: + lifetimeSecs = binary.BigEndian.Uint32(constraints[1:5]) + constraints = constraints[5:] + case agentConstrainConfirm: + confirmBeforeUse = true + constraints = constraints[1:] + case agentConstrainExtension: + var msg constrainExtensionAgentMsg + if err = ssh.Unmarshal(constraints, &msg); err != nil { + return 0, false, nil, err + } + extensions = append(extensions, ConstraintExtension{ + ExtensionName: msg.ExtensionName, + ExtensionDetails: msg.ExtensionDetails, + }) + constraints = msg.Rest + default: + return 0, false, nil, fmt.Errorf("unknown constraint type: %d", constraints[0]) + } + } + return +} + +func setConstraints(key *AddedKey, constraintBytes []byte) error { + lifetimeSecs, confirmBeforeUse, constraintExtensions, err := parseConstraints(constraintBytes) + if err != nil { + return err + } + + key.LifetimeSecs = lifetimeSecs + key.ConfirmBeforeUse = confirmBeforeUse + key.ConstraintExtensions = constraintExtensions + return nil +} + func parseRSAKey(req []byte) (*AddedKey, error) { var k rsaKeyMsg if err := ssh.Unmarshal(req, &k); err != nil { @@ -173,7 +211,11 @@ func parseRSAKey(req []byte) (*AddedKey, error) { } priv.Precompute() - return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil + addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil } func parseEd25519Key(req []byte) (*AddedKey, error) { @@ -182,7 +224,12 @@ func parseEd25519Key(req []byte) (*AddedKey, error) { return nil, err } priv := ed25519.PrivateKey(k.Priv) - return &AddedKey{PrivateKey: &priv, Comment: k.Comments}, nil + + addedKey := &AddedKey{PrivateKey: &priv, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil } func parseDSAKey(req []byte) (*AddedKey, error) { @@ -202,7 +249,11 @@ func parseDSAKey(req []byte) (*AddedKey, error) { X: k.X, } - return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil + addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil } func unmarshalECDSA(curveName string, keyBytes []byte, privScalar *big.Int) (priv *ecdsa.PrivateKey, err error) { @@ -243,7 +294,12 @@ func parseEd25519Cert(req []byte) (*AddedKey, error) { if !ok { return nil, errors.New("agent: bad ED25519 certificate") } - return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil + + addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil } func parseECDSAKey(req []byte) (*AddedKey, error) { @@ -257,7 +313,11 @@ func parseECDSAKey(req []byte) (*AddedKey, error) { return nil, err } - return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil + addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil } func parseRSACert(req []byte) (*AddedKey, error) { @@ -300,7 +360,11 @@ func parseRSACert(req []byte) (*AddedKey, error) { } priv.Precompute() - return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil + addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil } func parseDSACert(req []byte) (*AddedKey, error) { @@ -338,7 +402,11 @@ func parseDSACert(req []byte) (*AddedKey, error) { X: k.X, } - return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil + addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil } func parseECDSACert(req []byte) (*AddedKey, error) { @@ -371,7 +439,11 @@ func parseECDSACert(req []byte) (*AddedKey, error) { return nil, err } - return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil + addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil } func (s *server) insertIdentity(req []byte) error { diff --git a/vendor/golang.org/x/crypto/ssh/buffer.go b/vendor/golang.org/x/crypto/ssh/buffer.go index 6931b511..1ab07d07 100644 --- a/vendor/golang.org/x/crypto/ssh/buffer.go +++ b/vendor/golang.org/x/crypto/ssh/buffer.go @@ -51,13 +51,12 @@ func (b *buffer) write(buf []byte) { } // eof closes the buffer. Reads from the buffer once all -// the data has been consumed will receive os.EOF. -func (b *buffer) eof() error { +// the data has been consumed will receive io.EOF. +func (b *buffer) eof() { b.Cond.L.Lock() b.closed = true b.Cond.Signal() b.Cond.L.Unlock() - return nil } // Read reads data from the internal buffer in buf. Reads will block diff --git a/vendor/golang.org/x/crypto/ssh/certs.go b/vendor/golang.org/x/crypto/ssh/certs.go index 6331c94d..b1f02207 100644 --- a/vendor/golang.org/x/crypto/ssh/certs.go +++ b/vendor/golang.org/x/crypto/ssh/certs.go @@ -251,10 +251,18 @@ type CertChecker struct { // for user certificates. SupportedCriticalOptions []string - // IsAuthority should return true if the key is recognized as - // an authority. This allows for certificates to be signed by other - // certificates. - IsAuthority func(auth PublicKey) bool + // IsUserAuthority should return true if the key is recognized as an + // authority for the given user certificate. This allows for + // certificates to be signed by other certificates. This must be set + // if this CertChecker will be checking user certificates. + IsUserAuthority func(auth PublicKey) bool + + // IsHostAuthority should report whether the key is recognized as + // an authority for this host. This allows for certificates to be + // signed by other keys, and for those other keys to only be valid + // signers for particular hostnames. This must be set if this + // CertChecker will be checking host certificates. + IsHostAuthority func(auth PublicKey, address string) bool // Clock is used for verifying time stamps. If nil, time.Now // is used. @@ -268,7 +276,7 @@ type CertChecker struct { // HostKeyFallback is called when CertChecker.CheckHostKey encounters a // public key that is not a certificate. It must implement host key // validation or else, if nil, all such keys are rejected. - HostKeyFallback func(addr string, remote net.Addr, key PublicKey) error + HostKeyFallback HostKeyCallback // IsRevoked is called for each certificate so that revocation checking // can be implemented. It should return true if the given certificate @@ -290,8 +298,17 @@ func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) if cert.CertType != HostCert { return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType) } + if !c.IsHostAuthority(cert.SignatureKey, addr) { + return fmt.Errorf("ssh: no authorities for hostname: %v", addr) + } + + hostname, _, err := net.SplitHostPort(addr) + if err != nil { + return err + } - return c.CheckCert(addr, cert) + // Pass hostname only as principal for host certificates (consistent with OpenSSH) + return c.CheckCert(hostname, cert) } // Authenticate checks a user certificate. Authenticate can be used as @@ -308,6 +325,9 @@ func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permis if cert.CertType != UserCert { return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType) } + if !c.IsUserAuthority(cert.SignatureKey) { + return nil, fmt.Errorf("ssh: certificate signed by unrecognized authority") + } if err := c.CheckCert(conn.User(), cert); err != nil { return nil, err @@ -356,10 +376,6 @@ func (c *CertChecker) CheckCert(principal string, cert *Certificate) error { } } - if !c.IsAuthority(cert.SignatureKey) { - return fmt.Errorf("ssh: certificate signed by unrecognized authority") - } - clock := c.Clock if clock == nil { clock = time.Now diff --git a/vendor/golang.org/x/crypto/ssh/channel.go b/vendor/golang.org/x/crypto/ssh/channel.go index 6d709b50..195530ea 100644 --- a/vendor/golang.org/x/crypto/ssh/channel.go +++ b/vendor/golang.org/x/crypto/ssh/channel.go @@ -461,8 +461,8 @@ func (m *mux) newChannel(chanType string, direction channelDirection, extraData pending: newBuffer(), extPending: newBuffer(), direction: direction, - incomingRequests: make(chan *Request, 16), - msg: make(chan interface{}, 16), + incomingRequests: make(chan *Request, chanSize), + msg: make(chan interface{}, chanSize), chanType: chanType, extraData: extraData, mux: m, diff --git a/vendor/golang.org/x/crypto/ssh/cipher.go b/vendor/golang.org/x/crypto/ssh/cipher.go index 34d3917c..aed2b1f0 100644 --- a/vendor/golang.org/x/crypto/ssh/cipher.go +++ b/vendor/golang.org/x/crypto/ssh/cipher.go @@ -135,6 +135,7 @@ const prefixLen = 5 type streamPacketCipher struct { mac hash.Hash cipher cipher.Stream + etm bool // The following members are to avoid per-packet allocations. prefix [prefixLen]byte @@ -150,7 +151,14 @@ func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, err return nil, err } - s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) + var encryptedPaddingLength [1]byte + if s.mac != nil && s.etm { + copy(encryptedPaddingLength[:], s.prefix[4:5]) + s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5]) + } else { + s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) + } + length := binary.BigEndian.Uint32(s.prefix[0:4]) paddingLength := uint32(s.prefix[4]) @@ -159,7 +167,12 @@ func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, err s.mac.Reset() binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) s.mac.Write(s.seqNumBytes[:]) - s.mac.Write(s.prefix[:]) + if s.etm { + s.mac.Write(s.prefix[:4]) + s.mac.Write(encryptedPaddingLength[:]) + } else { + s.mac.Write(s.prefix[:]) + } macSize = uint32(s.mac.Size()) } @@ -184,10 +197,17 @@ func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, err } mac := s.packetData[length-1:] data := s.packetData[:length-1] + + if s.mac != nil && s.etm { + s.mac.Write(data) + } + s.cipher.XORKeyStream(data, data) if s.mac != nil { - s.mac.Write(data) + if !s.etm { + s.mac.Write(data) + } s.macResult = s.mac.Sum(s.macResult[:0]) if subtle.ConstantTimeCompare(s.macResult, mac) != 1 { return nil, errors.New("ssh: MAC failure") @@ -203,7 +223,13 @@ func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Rea return errors.New("ssh: packet too large") } - paddingLength := packetSizeMultiple - (prefixLen+len(packet))%packetSizeMultiple + aadlen := 0 + if s.mac != nil && s.etm { + // packet length is not encrypted for EtM modes + aadlen = 4 + } + + paddingLength := packetSizeMultiple - (prefixLen+len(packet)-aadlen)%packetSizeMultiple if paddingLength < 4 { paddingLength += packetSizeMultiple } @@ -220,15 +246,37 @@ func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Rea s.mac.Reset() binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) s.mac.Write(s.seqNumBytes[:]) + + if s.etm { + // For EtM algorithms, the packet length must stay unencrypted, + // but the following data (padding length) must be encrypted + s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5]) + } + s.mac.Write(s.prefix[:]) - s.mac.Write(packet) - s.mac.Write(padding) + + if !s.etm { + // For non-EtM algorithms, the algorithm is applied on unencrypted data + s.mac.Write(packet) + s.mac.Write(padding) + } + } + + if !(s.mac != nil && s.etm) { + // For EtM algorithms, the padding length has already been encrypted + // and the packet length must remain unencrypted + s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) } - s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) s.cipher.XORKeyStream(packet, packet) s.cipher.XORKeyStream(padding, padding) + if s.mac != nil && s.etm { + // For EtM algorithms, packet and padding must be encrypted + s.mac.Write(packet) + s.mac.Write(padding) + } + if _, err := w.Write(s.prefix[:]); err != nil { return err } @@ -256,7 +304,7 @@ type gcmCipher struct { buf []byte } -func newGCMCipher(iv, key, macKey []byte) (packetCipher, error) { +func newGCMCipher(iv, key []byte) (packetCipher, error) { c, err := aes.NewCipher(key) if err != nil { return nil, err @@ -344,7 +392,9 @@ func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { c.incIV() padding := plain[0] - if padding < 4 || padding >= 20 { + if padding < 4 { + // padding is a byte, so it automatically satisfies + // the maximum size, which is 255. return nil, fmt.Errorf("ssh: illegal padding %d", padding) } diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go index 0212a20c..6fd19945 100644 --- a/vendor/golang.org/x/crypto/ssh/client.go +++ b/vendor/golang.org/x/crypto/ssh/client.go @@ -5,15 +5,17 @@ package ssh import ( + "bytes" "errors" "fmt" "net" + "os" "sync" "time" ) // Client implements a traditional SSH client that supports shells, -// subprocesses, port forwarding and tunneled dialing. +// subprocesses, TCP port/streamlocal forwarding and tunneled dialing. type Client struct { Conn @@ -40,7 +42,7 @@ func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel { return nil } - ch = make(chan NewChannel, 16) + ch = make(chan NewChannel, chanSize) c.channelHandlers[channelType] = ch return ch } @@ -59,6 +61,7 @@ func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client { conn.forwards.closeAll() }() go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip")) + go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-streamlocal@openssh.com")) return conn } @@ -68,6 +71,11 @@ func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client { func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) { fullConf := *config fullConf.SetDefaults() + if fullConf.HostKeyCallback == nil { + c.Close() + return nil, nil, nil, errors.New("ssh: must specify HostKeyCallback") + } + conn := &connection{ sshConn: sshConn{conn: c}, } @@ -97,13 +105,11 @@ func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) e c.transport = newClientTransport( newTransport(c.sshConn.conn, config.Rand, true /* is client */), c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr()) - if err := c.transport.requestInitialKeyChange(); err != nil { + if err := c.transport.waitSession(); err != nil { return err } - // We just did the key change, so the session ID is established. c.sessionID = c.transport.getSessionID() - return c.clientAuthenticate(config) } @@ -175,6 +181,17 @@ func Dial(network, addr string, config *ClientConfig) (*Client, error) { return NewClient(c, chans, reqs), nil } +// HostKeyCallback is the function type used for verifying server +// keys. A HostKeyCallback must return nil if the host key is OK, or +// an error to reject it. It receives the hostname as passed to Dial +// or NewClientConn. The remote address is the RemoteAddr of the +// net.Conn underlying the the SSH connection. +type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error + +// BannerCallback is the function type used for treat the banner sent by +// the server. A BannerCallback receives the message sent by the remote server. +type BannerCallback func(message string) error + // A ClientConfig structure is used to configure a Client. It must not be // modified after having been passed to an SSH function. type ClientConfig struct { @@ -190,10 +207,18 @@ type ClientConfig struct { // be used during authentication. Auth []AuthMethod - // HostKeyCallback, if not nil, is called during the cryptographic - // handshake to validate the server's host key. A nil HostKeyCallback - // implies that all host keys are accepted. - HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error + // HostKeyCallback is called during the cryptographic + // handshake to validate the server's host key. The client + // configuration must supply this callback for the connection + // to succeed. The functions InsecureIgnoreHostKey or + // FixedHostKey can be used for simplistic host key checks. + HostKeyCallback HostKeyCallback + + // BannerCallback is called during the SSH dance to display a custom + // server's message. The client configuration can supply this callback to + // handle it as wished. The function BannerDisplayStderr can be used for + // simplistic display on Stderr. + BannerCallback BannerCallback // ClientVersion contains the version identification string that will // be used for the connection. If empty, a reasonable default is used. @@ -211,3 +236,43 @@ type ClientConfig struct { // A Timeout of zero means no timeout. Timeout time.Duration } + +// InsecureIgnoreHostKey returns a function that can be used for +// ClientConfig.HostKeyCallback to accept any host key. It should +// not be used for production code. +func InsecureIgnoreHostKey() HostKeyCallback { + return func(hostname string, remote net.Addr, key PublicKey) error { + return nil + } +} + +type fixedHostKey struct { + key PublicKey +} + +func (f *fixedHostKey) check(hostname string, remote net.Addr, key PublicKey) error { + if f.key == nil { + return fmt.Errorf("ssh: required host key was nil") + } + if !bytes.Equal(key.Marshal(), f.key.Marshal()) { + return fmt.Errorf("ssh: host key mismatch") + } + return nil +} + +// FixedHostKey returns a function for use in +// ClientConfig.HostKeyCallback to accept only a specific host key. +func FixedHostKey(key PublicKey) HostKeyCallback { + hk := &fixedHostKey{key} + return hk.check +} + +// BannerDisplayStderr returns a function that can be used for +// ClientConfig.BannerCallback to display banners on os.Stderr. +func BannerDisplayStderr() BannerCallback { + return func(banner string) error { + _, err := os.Stderr.WriteString(banner) + + return err + } +} diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go index 294af0d4..a1252cb9 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth.go @@ -30,8 +30,10 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error { // then any untried methods suggested by the server. tried := make(map[string]bool) var lastMethods []string + + sessionID := c.transport.getSessionID() for auth := AuthMethod(new(noneAuth)); auth != nil; { - ok, methods, err := auth.auth(c.transport.getSessionID(), config.User, c.transport, config.Rand) + ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand) if err != nil { return err } @@ -177,31 +179,26 @@ func (cb publicKeyCallback) method() string { } func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { - // Authentication is performed in two stages. The first stage sends an - // enquiry to test if each key is acceptable to the remote. The second - // stage attempts to authenticate with the valid keys obtained in the - // first stage. + // Authentication is performed by sending an enquiry to test if a key is + // acceptable to the remote. If the key is acceptable, the client will + // attempt to authenticate with the valid key. If not the client will repeat + // the process with the remaining keys. signers, err := cb() if err != nil { return false, nil, err } - var validKeys []Signer + var methods []string for _, signer := range signers { - if ok, err := validateKey(signer.PublicKey(), user, c); ok { - validKeys = append(validKeys, signer) - } else { - if err != nil { - return false, nil, err - } + ok, err := validateKey(signer.PublicKey(), user, c) + if err != nil { + return false, nil, err + } + if !ok { + continue } - } - // methods that may continue if this auth is not successful. - var methods []string - for _, signer := range validKeys { pub := signer.PublicKey() - pubKey := pub.Marshal() sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{ User: user, @@ -234,13 +231,29 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand if err != nil { return false, nil, err } - if success { + + // If authentication succeeds or the list of available methods does not + // contain the "publickey" method, do not attempt to authenticate with any + // other keys. According to RFC 4252 Section 7, the latter can occur when + // additional authentication methods are required. + if success || !containsMethod(methods, cb.method()) { return success, methods, err } } + return false, methods, nil } +func containsMethod(methods []string, method string) bool { + for _, m := range methods { + if m == method { + return true + } + } + + return false +} + // validateKey validates the key provided is acceptable to the server. func validateKey(key PublicKey, user string, c packetConn) (bool, error) { pubKey := key.Marshal() @@ -270,7 +283,9 @@ func confirmKeyAck(key PublicKey, c packetConn) (bool, error) { } switch packet[0] { case msgUserAuthBanner: - // TODO(gpaul): add callback to present the banner to the user + if err := handleBannerResponse(c, packet); err != nil { + return false, err + } case msgUserAuthPubKeyOk: var msg userAuthPubKeyOkMsg if err := Unmarshal(packet, &msg); err != nil { @@ -312,7 +327,9 @@ func handleAuthResponse(c packetConn) (bool, []string, error) { switch packet[0] { case msgUserAuthBanner: - // TODO: add callback to present the banner to the user + if err := handleBannerResponse(c, packet); err != nil { + return false, nil, err + } case msgUserAuthFailure: var msg userAuthFailureMsg if err := Unmarshal(packet, &msg); err != nil { @@ -327,6 +344,24 @@ func handleAuthResponse(c packetConn) (bool, []string, error) { } } +func handleBannerResponse(c packetConn, packet []byte) error { + var msg userAuthBannerMsg + if err := Unmarshal(packet, &msg); err != nil { + return err + } + + transport, ok := c.(*handshakeTransport) + if !ok { + return nil + } + + if transport.bannerCallback != nil { + return transport.bannerCallback(msg.Message) + } + + return nil +} + // KeyboardInteractiveChallenge should print questions, optionally // disabling echoing (e.g. for passwords), and return all the answers. // Challenge may be called multiple times in a single session. After @@ -336,7 +371,7 @@ func handleAuthResponse(c packetConn) (bool, []string, error) { // both CLI and GUI environments. type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error) -// KeyboardInteractive returns a AuthMethod using a prompt/response +// KeyboardInteractive returns an AuthMethod using a prompt/response // sequence controlled by the server. func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod { return challenge @@ -372,7 +407,9 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe // like handleAuthResponse, but with less options. switch packet[0] { case msgUserAuthBanner: - // TODO: Print banners during userauth. + if err := handleBannerResponse(c, packet); err != nil { + return false, nil, err + } continue case msgUserAuthInfoRequest: // OK diff --git a/vendor/golang.org/x/crypto/ssh/common.go b/vendor/golang.org/x/crypto/ssh/common.go index 2c72ab54..dc39e4d2 100644 --- a/vendor/golang.org/x/crypto/ssh/common.go +++ b/vendor/golang.org/x/crypto/ssh/common.go @@ -9,6 +9,7 @@ import ( "crypto/rand" "fmt" "io" + "math" "sync" _ "crypto/sha1" @@ -40,7 +41,7 @@ var supportedKexAlgos = []string{ kexAlgoDH14SHA1, kexAlgoDH1SHA1, } -// supportedKexAlgos specifies the supported host-key algorithms (i.e. methods +// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods // of authenticating servers) in preference order. var supportedHostKeyAlgos = []string{ CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, @@ -56,7 +57,7 @@ var supportedHostKeyAlgos = []string{ // This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed // because they have reached the end of their useful life. var supportedMACs = []string{ - "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96", + "hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96", } var supportedCompressions = []string{compressionNone} @@ -104,6 +105,21 @@ type directionAlgorithms struct { Compression string } +// rekeyBytes returns a rekeying intervals in bytes. +func (a *directionAlgorithms) rekeyBytes() int64 { + // According to RFC4344 block ciphers should rekey after + // 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is + // 128. + switch a.Cipher { + case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcmCipherID, aes128cbcID: + return 16 * (1 << 32) + + } + + // For others, stick with RFC4253 recommendation to rekey after 1 Gb of data. + return 1 << 30 +} + type algorithms struct { kex string hostKey string @@ -171,7 +187,7 @@ type Config struct { // The maximum number of bytes sent or received after which a // new key is negotiated. It must be at least 256. If - // unspecified, 1 gigabyte is used. + // unspecified, a size suitable for the chosen cipher is used. RekeyThreshold uint64 // The allowed key exchanges algorithms. If unspecified then a @@ -215,11 +231,12 @@ func (c *Config) SetDefaults() { } if c.RekeyThreshold == 0 { - // RFC 4253, section 9 suggests rekeying after 1G. - c.RekeyThreshold = 1 << 30 - } - if c.RekeyThreshold < minRekeyThreshold { + // cipher specific default + } else if c.RekeyThreshold < minRekeyThreshold { c.RekeyThreshold = minRekeyThreshold + } else if c.RekeyThreshold >= math.MaxInt64 { + // Avoid weirdness if somebody uses -1 as a threshold. + c.RekeyThreshold = math.MaxInt64 } } diff --git a/vendor/golang.org/x/crypto/ssh/connection.go b/vendor/golang.org/x/crypto/ssh/connection.go index e786f2f9..fd6b0681 100644 --- a/vendor/golang.org/x/crypto/ssh/connection.go +++ b/vendor/golang.org/x/crypto/ssh/connection.go @@ -25,7 +25,7 @@ type ConnMetadata interface { // User returns the user ID for this connection. User() string - // SessionID returns the sesson hash, also denoted by H. + // SessionID returns the session hash, also denoted by H. SessionID() []byte // ClientVersion returns the client's version string as hashed diff --git a/vendor/golang.org/x/crypto/ssh/doc.go b/vendor/golang.org/x/crypto/ssh/doc.go index d6be8946..67b7322c 100644 --- a/vendor/golang.org/x/crypto/ssh/doc.go +++ b/vendor/golang.org/x/crypto/ssh/doc.go @@ -14,5 +14,8 @@ others. References: [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 + +This package does not fall under the stability promise of the Go language itself, +so its API may be changed when pressing needs arise. */ package ssh // import "golang.org/x/crypto/ssh" diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go index 37d42e47..4f7912ec 100644 --- a/vendor/golang.org/x/crypto/ssh/handshake.go +++ b/vendor/golang.org/x/crypto/ssh/handshake.go @@ -19,6 +19,11 @@ import ( // messages are wrong when using ECDH. const debugHandshake = false +// chanSize sets the amount of buffering SSH connections. This is +// primarily for testing: setting chanSize=0 uncovers deadlocks more +// quickly. +const chanSize = 16 + // keyingTransport is a packet based transport that supports key // changes. It need not be thread-safe. It should pass through // msgNewKeys in both directions. @@ -53,34 +58,65 @@ type handshakeTransport struct { incoming chan []byte readError error + mu sync.Mutex + writeError error + sentInitPacket []byte + sentInitMsg *kexInitMsg + pendingPackets [][]byte // Used when a key exchange is in progress. + + // If the read loop wants to schedule a kex, it pings this + // channel, and the write loop will send out a kex + // message. + requestKex chan struct{} + + // If the other side requests or confirms a kex, its kexInit + // packet is sent here for the write loop to find it. + startKex chan *pendingKex + // data for host key checking - hostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error + hostKeyCallback HostKeyCallback dialAddress string remoteAddr net.Addr - readSinceKex uint64 + // bannerCallback is non-empty if we are the client and it has been set in + // ClientConfig. In that case it is called during the user authentication + // dance to handle a custom server's message. + bannerCallback BannerCallback + + // Algorithms agreed in the last key exchange. + algorithms *algorithms - // Protects the writing side of the connection - mu sync.Mutex - cond *sync.Cond - sentInitPacket []byte - sentInitMsg *kexInitMsg - writtenSinceKex uint64 - writeError error + readPacketsLeft uint32 + readBytesLeft int64 + + writePacketsLeft uint32 + writeBytesLeft int64 // The session ID or nil if first kex did not complete yet. sessionID []byte } +type pendingKex struct { + otherInit []byte + done chan error +} + func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport { t := &handshakeTransport{ conn: conn, serverVersion: serverVersion, clientVersion: clientVersion, - incoming: make(chan []byte, 16), - config: config, + incoming: make(chan []byte, chanSize), + requestKex: make(chan struct{}, 1), + startKex: make(chan *pendingKex, 1), + + config: config, } - t.cond = sync.NewCond(&t.mu) + t.resetReadThresholds() + t.resetWriteThresholds() + + // We always start with a mandatory key exchange. + t.requestKex <- struct{}{} return t } @@ -89,12 +125,14 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt t.dialAddress = dialAddr t.remoteAddr = addr t.hostKeyCallback = config.HostKeyCallback + t.bannerCallback = config.BannerCallback if config.HostKeyAlgorithms != nil { t.hostKeyAlgorithms = config.HostKeyAlgorithms } else { t.hostKeyAlgorithms = supportedHostKeyAlgos } go t.readLoop() + go t.kexLoop() return t } @@ -102,6 +140,7 @@ func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byt t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) t.hostKeys = config.hostKeys go t.readLoop() + go t.kexLoop() return t } @@ -109,6 +148,20 @@ func (t *handshakeTransport) getSessionID() []byte { return t.sessionID } +// waitSession waits for the session to be established. This should be +// the first thing to call after instantiating handshakeTransport. +func (t *handshakeTransport) waitSession() error { + p, err := t.readPacket() + if err != nil { + return err + } + if p[0] != msgNewKeys { + return fmt.Errorf("ssh: first packet should be msgNewKeys") + } + + return nil +} + func (t *handshakeTransport) id() string { if len(t.hostKeys) > 0 { return "server" @@ -116,6 +169,20 @@ func (t *handshakeTransport) id() string { return "client" } +func (t *handshakeTransport) printPacket(p []byte, write bool) { + action := "got" + if write { + action = "sent" + } + + if p[0] == msgChannelData || p[0] == msgChannelExtendedData { + log.Printf("%s %s data (packet %d bytes)", t.id(), action, len(p)) + } else { + msg, err := decode(p) + log.Printf("%s %s %T %v (%v)", t.id(), action, msg, msg, err) + } +} + func (t *handshakeTransport) readPacket() ([]byte, error) { p, ok := <-t.incoming if !ok { @@ -125,8 +192,10 @@ func (t *handshakeTransport) readPacket() ([]byte, error) { } func (t *handshakeTransport) readLoop() { + first := true for { - p, err := t.readOnePacket() + p, err := t.readOnePacket(first) + first = false if err != nil { t.readError = err close(t.incoming) @@ -138,67 +207,217 @@ func (t *handshakeTransport) readLoop() { t.incoming <- p } - // If we can't read, declare the writing part dead too. + // Stop writers too. + t.recordWriteError(t.readError) + + // Unblock the writer should it wait for this. + close(t.startKex) + + // Don't close t.requestKex; it's also written to from writePacket. +} + +func (t *handshakeTransport) pushPacket(p []byte) error { + if debugHandshake { + t.printPacket(p, true) + } + return t.conn.writePacket(p) +} + +func (t *handshakeTransport) getWriteError() error { t.mu.Lock() defer t.mu.Unlock() - if t.writeError == nil { - t.writeError = t.readError + return t.writeError +} + +func (t *handshakeTransport) recordWriteError(err error) { + t.mu.Lock() + defer t.mu.Unlock() + if t.writeError == nil && err != nil { + t.writeError = err } - t.cond.Broadcast() } -func (t *handshakeTransport) readOnePacket() ([]byte, error) { - if t.readSinceKex > t.config.RekeyThreshold { - if err := t.requestKeyChange(); err != nil { - return nil, err +func (t *handshakeTransport) requestKeyExchange() { + select { + case t.requestKex <- struct{}{}: + default: + // something already requested a kex, so do nothing. + } +} + +func (t *handshakeTransport) resetWriteThresholds() { + t.writePacketsLeft = packetRekeyThreshold + if t.config.RekeyThreshold > 0 { + t.writeBytesLeft = int64(t.config.RekeyThreshold) + } else if t.algorithms != nil { + t.writeBytesLeft = t.algorithms.w.rekeyBytes() + } else { + t.writeBytesLeft = 1 << 30 + } +} + +func (t *handshakeTransport) kexLoop() { + +write: + for t.getWriteError() == nil { + var request *pendingKex + var sent bool + + for request == nil || !sent { + var ok bool + select { + case request, ok = <-t.startKex: + if !ok { + break write + } + case <-t.requestKex: + break + } + + if !sent { + if err := t.sendKexInit(); err != nil { + t.recordWriteError(err) + break + } + sent = true + } + } + + if err := t.getWriteError(); err != nil { + if request != nil { + request.done <- err + } + break + } + + // We're not servicing t.requestKex, but that is OK: + // we never block on sending to t.requestKex. + + // We're not servicing t.startKex, but the remote end + // has just sent us a kexInitMsg, so it can't send + // another key change request, until we close the done + // channel on the pendingKex request. + + err := t.enterKeyExchange(request.otherInit) + + t.mu.Lock() + t.writeError = err + t.sentInitPacket = nil + t.sentInitMsg = nil + + t.resetWriteThresholds() + + // we have completed the key exchange. Since the + // reader is still blocked, it is safe to clear out + // the requestKex channel. This avoids the situation + // where: 1) we consumed our own request for the + // initial kex, and 2) the kex from the remote side + // caused another send on the requestKex channel, + clear: + for { + select { + case <-t.requestKex: + // + default: + break clear + } } + + request.done <- t.writeError + + // kex finished. Push packets that we received while + // the kex was in progress. Don't look at t.startKex + // and don't increment writtenSinceKex: if we trigger + // another kex while we are still busy with the last + // one, things will become very confusing. + for _, p := range t.pendingPackets { + t.writeError = t.pushPacket(p) + if t.writeError != nil { + break + } + } + t.pendingPackets = t.pendingPackets[:0] + t.mu.Unlock() } + // drain startKex channel. We don't service t.requestKex + // because nobody does blocking sends there. + go func() { + for init := range t.startKex { + init.done <- t.writeError + } + }() + + // Unblock reader. + t.conn.Close() +} + +// The protocol uses uint32 for packet counters, so we can't let them +// reach 1<<32. We will actually read and write more packets than +// this, though: the other side may send more packets, and after we +// hit this limit on writing we will send a few more packets for the +// key exchange itself. +const packetRekeyThreshold = (1 << 31) + +func (t *handshakeTransport) resetReadThresholds() { + t.readPacketsLeft = packetRekeyThreshold + if t.config.RekeyThreshold > 0 { + t.readBytesLeft = int64(t.config.RekeyThreshold) + } else if t.algorithms != nil { + t.readBytesLeft = t.algorithms.r.rekeyBytes() + } else { + t.readBytesLeft = 1 << 30 + } +} + +func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) { p, err := t.conn.readPacket() if err != nil { return nil, err } - t.readSinceKex += uint64(len(p)) + if t.readPacketsLeft > 0 { + t.readPacketsLeft-- + } else { + t.requestKeyExchange() + } + + if t.readBytesLeft > 0 { + t.readBytesLeft -= int64(len(p)) + } else { + t.requestKeyExchange() + } + if debugHandshake { - if p[0] == msgChannelData || p[0] == msgChannelExtendedData { - log.Printf("%s got data (packet %d bytes)", t.id(), len(p)) - } else { - msg, err := decode(p) - log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err) - } + t.printPacket(p, false) } + + if first && p[0] != msgKexInit { + return nil, fmt.Errorf("ssh: first packet should be msgKexInit") + } + if p[0] != msgKexInit { return p, nil } - t.mu.Lock() - firstKex := t.sessionID == nil - err = t.enterKeyExchangeLocked(p) - if err != nil { - // drop connection - t.conn.Close() - t.writeError = err + kex := pendingKex{ + done: make(chan error, 1), + otherInit: p, } + t.startKex <- &kex + err = <-kex.done if debugHandshake { log.Printf("%s exited key exchange (first %v), err %v", t.id(), firstKex, err) } - // Unblock writers. - t.sentInitMsg = nil - t.sentInitPacket = nil - t.cond.Broadcast() - t.writtenSinceKex = 0 - t.mu.Unlock() - if err != nil { return nil, err } - t.readSinceKex = 0 + t.resetReadThresholds() // By default, a key exchange is hidden from higher layers by // translating it into msgIgnore. @@ -213,61 +432,16 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) { return successPacket, nil } -// keyChangeCategory describes whether a key exchange is the first on a -// connection, or a subsequent one. -type keyChangeCategory bool - -const ( - firstKeyExchange keyChangeCategory = true - subsequentKeyExchange keyChangeCategory = false -) - -// sendKexInit sends a key change message, and returns the message -// that was sent. After initiating the key change, all writes will be -// blocked until the change is done, and a failed key change will -// close the underlying transport. This function is safe for -// concurrent use by multiple goroutines. -func (t *handshakeTransport) sendKexInit(isFirst keyChangeCategory) error { - var err error - +// sendKexInit sends a key change message. +func (t *handshakeTransport) sendKexInit() error { t.mu.Lock() - // If this is the initial key change, but we already have a sessionID, - // then do nothing because the key exchange has already completed - // asynchronously. - if !isFirst || t.sessionID == nil { - _, _, err = t.sendKexInitLocked(isFirst) - } - t.mu.Unlock() - if err != nil { - return err - } - if isFirst { - if packet, err := t.readPacket(); err != nil { - return err - } else if packet[0] != msgNewKeys { - return unexpectedMessageError(msgNewKeys, packet[0]) - } - } - return nil -} - -func (t *handshakeTransport) requestInitialKeyChange() error { - return t.sendKexInit(firstKeyExchange) -} - -func (t *handshakeTransport) requestKeyChange() error { - return t.sendKexInit(subsequentKeyExchange) -} - -// sendKexInitLocked sends a key change message. t.mu must be locked -// while this happens. -func (t *handshakeTransport) sendKexInitLocked(isFirst keyChangeCategory) (*kexInitMsg, []byte, error) { - // kexInits may be sent either in response to the other side, - // or because our side wants to initiate a key change, so we - // may have already sent a kexInit. In that case, don't send a - // second kexInit. + defer t.mu.Unlock() if t.sentInitMsg != nil { - return t.sentInitMsg, t.sentInitPacket, nil + // kexInits may be sent either in response to the other side, + // or because our side wants to initiate a key change, so we + // may have already sent a kexInit. In that case, don't send a + // second kexInit. + return nil } msg := &kexInitMsg{ @@ -295,53 +469,65 @@ func (t *handshakeTransport) sendKexInitLocked(isFirst keyChangeCategory) (*kexI packetCopy := make([]byte, len(packet)) copy(packetCopy, packet) - if err := t.conn.writePacket(packetCopy); err != nil { - return nil, nil, err + if err := t.pushPacket(packetCopy); err != nil { + return err } t.sentInitMsg = msg t.sentInitPacket = packet - return msg, packet, nil + + return nil } func (t *handshakeTransport) writePacket(p []byte) error { + switch p[0] { + case msgKexInit: + return errors.New("ssh: only handshakeTransport can send kexInit") + case msgNewKeys: + return errors.New("ssh: only handshakeTransport can send newKeys") + } + t.mu.Lock() defer t.mu.Unlock() + if t.writeError != nil { + return t.writeError + } - if t.writtenSinceKex > t.config.RekeyThreshold { - t.sendKexInitLocked(subsequentKeyExchange) + if t.sentInitMsg != nil { + // Copy the packet so the writer can reuse the buffer. + cp := make([]byte, len(p)) + copy(cp, p) + t.pendingPackets = append(t.pendingPackets, cp) + return nil } - for t.sentInitMsg != nil && t.writeError == nil { - t.cond.Wait() + + if t.writeBytesLeft > 0 { + t.writeBytesLeft -= int64(len(p)) + } else { + t.requestKeyExchange() } - if t.writeError != nil { - return t.writeError + + if t.writePacketsLeft > 0 { + t.writePacketsLeft-- + } else { + t.requestKeyExchange() } - t.writtenSinceKex += uint64(len(p)) - switch p[0] { - case msgKexInit: - return errors.New("ssh: only handshakeTransport can send kexInit") - case msgNewKeys: - return errors.New("ssh: only handshakeTransport can send newKeys") - default: - return t.conn.writePacket(p) + if err := t.pushPacket(p); err != nil { + t.writeError = err } + + return nil } func (t *handshakeTransport) Close() error { return t.conn.Close() } -// enterKeyExchange runs the key exchange. t.mu must be held while running this. -func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) error { +func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { if debugHandshake { log.Printf("%s entered key exchange", t.id()) } - myInit, myInitPacket, err := t.sendKexInitLocked(subsequentKeyExchange) - if err != nil { - return err - } otherInit := &kexInitMsg{} if err := Unmarshal(otherInitPacket, otherInit); err != nil { @@ -352,20 +538,20 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro clientVersion: t.clientVersion, serverVersion: t.serverVersion, clientKexInit: otherInitPacket, - serverKexInit: myInitPacket, + serverKexInit: t.sentInitPacket, } clientInit := otherInit - serverInit := myInit + serverInit := t.sentInitMsg if len(t.hostKeys) == 0 { - clientInit = myInit - serverInit = otherInit + clientInit, serverInit = serverInit, clientInit - magics.clientKexInit = myInitPacket + magics.clientKexInit = t.sentInitPacket magics.serverKexInit = otherInitPacket } - algs, err := findAgreedAlgorithms(clientInit, serverInit) + var err error + t.algorithms, err = findAgreedAlgorithms(clientInit, serverInit) if err != nil { return err } @@ -388,16 +574,16 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro } } - kex, ok := kexAlgoMap[algs.kex] + kex, ok := kexAlgoMap[t.algorithms.kex] if !ok { - return fmt.Errorf("ssh: unexpected key exchange algorithm %v", algs.kex) + return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.kex) } var result *kexResult if len(t.hostKeys) > 0 { - result, err = t.server(kex, algs, &magics) + result, err = t.server(kex, t.algorithms, &magics) } else { - result, err = t.client(kex, algs, &magics) + result, err = t.client(kex, t.algorithms, &magics) } if err != nil { @@ -409,7 +595,9 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro } result.SessionID = t.sessionID - t.conn.prepareKeyChange(algs, result) + if err := t.conn.prepareKeyChange(t.algorithms, result); err != nil { + return err + } if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { return err } @@ -449,11 +637,9 @@ func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics * return nil, err } - if t.hostKeyCallback != nil { - err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey) - if err != nil { - return nil, err - } + err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey) + if err != nil { + return nil, err } return result, nil diff --git a/vendor/golang.org/x/crypto/ssh/kex.go b/vendor/golang.org/x/crypto/ssh/kex.go index c87fbebf..f91c2770 100644 --- a/vendor/golang.org/x/crypto/ssh/kex.go +++ b/vendor/golang.org/x/crypto/ssh/kex.go @@ -383,8 +383,8 @@ func init() { // 4253 and Oakley Group 2 in RFC 2409. p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ - g: new(big.Int).SetInt64(2), - p: p, + g: new(big.Int).SetInt64(2), + p: p, pMinus1: new(big.Int).Sub(p, bigOne), } @@ -393,8 +393,8 @@ func init() { p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ - g: new(big.Int).SetInt64(2), - p: p, + g: new(big.Int).SetInt64(2), + p: p, pMinus1: new(big.Int).Sub(p, bigOne), } diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go index 21f7d0d2..b682c174 100644 --- a/vendor/golang.org/x/crypto/ssh/keys.go +++ b/vendor/golang.org/x/crypto/ssh/keys.go @@ -367,6 +367,17 @@ func (r *dsaPublicKey) Type() string { return "ssh-dss" } +func checkDSAParams(param *dsa.Parameters) error { + // SSH specifies FIPS 186-2, which only provided a single size + // (1024 bits) DSA key. FIPS 186-3 allows for larger key + // sizes, which would confuse SSH. + if l := param.P.BitLen(); l != 1024 { + return fmt.Errorf("ssh: unsupported DSA key size %d", l) + } + + return nil +} + // parseDSA parses an DSA key according to RFC 4253, section 6.6. func parseDSA(in []byte) (out PublicKey, rest []byte, err error) { var w struct { @@ -377,13 +388,18 @@ func parseDSA(in []byte) (out PublicKey, rest []byte, err error) { return nil, nil, err } + param := dsa.Parameters{ + P: w.P, + Q: w.Q, + G: w.G, + } + if err := checkDSAParams(¶m); err != nil { + return nil, nil, err + } + key := &dsaPublicKey{ - Parameters: dsa.Parameters{ - P: w.P, - Q: w.Q, - G: w.G, - }, - Y: w.Y, + Parameters: param, + Y: w.Y, } return key, w.Rest, nil } @@ -630,19 +646,28 @@ func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey { } // NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey, -// *ecdsa.PrivateKey or any other crypto.Signer and returns a corresponding -// Signer instance. ECDSA keys must use P-256, P-384 or P-521. +// *ecdsa.PrivateKey or any other crypto.Signer and returns a +// corresponding Signer instance. ECDSA keys must use P-256, P-384 or +// P-521. DSA keys must use parameter size L1024N160. func NewSignerFromKey(key interface{}) (Signer, error) { switch key := key.(type) { case crypto.Signer: return NewSignerFromSigner(key) case *dsa.PrivateKey: - return &dsaPrivateKey{key}, nil + return newDSAPrivateKey(key) default: return nil, fmt.Errorf("ssh: unsupported key type %T", key) } } +func newDSAPrivateKey(key *dsa.PrivateKey) (Signer, error) { + if err := checkDSAParams(&key.PublicKey.Parameters); err != nil { + return nil, err + } + + return &dsaPrivateKey{key}, nil +} + type wrappedSigner struct { signer crypto.Signer pubKey PublicKey @@ -756,6 +781,18 @@ func ParsePrivateKey(pemBytes []byte) (Signer, error) { return NewSignerFromKey(key) } +// ParsePrivateKeyWithPassphrase returns a Signer from a PEM encoded private +// key and passphrase. It supports the same keys as +// ParseRawPrivateKeyWithPassphrase. +func ParsePrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (Signer, error) { + key, err := ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase) + if err != nil { + return nil, err + } + + return NewSignerFromKey(key) +} + // encryptedBlock tells whether a private key is // encrypted by examining its Proc-Type header // for a mention of ENCRYPTED @@ -790,6 +827,43 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { } } +// ParseRawPrivateKeyWithPassphrase returns a private key decrypted with +// passphrase from a PEM encoded private key. If wrong passphrase, return +// x509.IncorrectPasswordError. +func ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (interface{}, error) { + block, _ := pem.Decode(pemBytes) + if block == nil { + return nil, errors.New("ssh: no key found") + } + buf := block.Bytes + + if encryptedBlock(block) { + if x509.IsEncryptedPEMBlock(block) { + var err error + buf, err = x509.DecryptPEMBlock(block, passPhrase) + if err != nil { + if err == x509.IncorrectPasswordError { + return nil, err + } + return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err) + } + } + } + + switch block.Type { + case "RSA PRIVATE KEY": + return x509.ParsePKCS1PrivateKey(buf) + case "EC PRIVATE KEY": + return x509.ParseECPrivateKey(buf) + case "DSA PRIVATE KEY": + return ParseDSAPrivateKey(buf) + case "OPENSSH PRIVATE KEY": + return parseOpenSSHPrivateKey(buf) + default: + return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) + } +} + // ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as // specified by the OpenSSL DSA man page. func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { @@ -798,8 +872,8 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { P *big.Int Q *big.Int G *big.Int - Priv *big.Int Pub *big.Int + Priv *big.Int } rest, err := asn1.Unmarshal(der, &k) if err != nil { @@ -816,15 +890,15 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { Q: k.Q, G: k.G, }, - Y: k.Priv, + Y: k.Pub, }, - X: k.Pub, + X: k.Priv, }, nil } // Implemented based on the documentation at // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key -func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) { +func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) { magic := append([]byte("openssh-key-v1"), 0) if !bytes.Equal(magic, key[0:len(magic)]) { return nil, errors.New("ssh: invalid openssh private key format") @@ -844,14 +918,15 @@ func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) { return nil, err } + if w.KdfName != "none" || w.CipherName != "none" { + return nil, errors.New("ssh: cannot decode encrypted private keys") + } + pk1 := struct { Check1 uint32 Check2 uint32 Keytype string - Pub []byte - Priv []byte - Comment string - Pad []byte `ssh:"rest"` + Rest []byte `ssh:"rest"` }{} if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil { @@ -862,24 +937,75 @@ func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) { return nil, errors.New("ssh: checkint mismatch") } - // we only handle ed25519 keys currently - if pk1.Keytype != KeyAlgoED25519 { - return nil, errors.New("ssh: unhandled key type") - } + // we only handle ed25519 and rsa keys currently + switch pk1.Keytype { + case KeyAlgoRSA: + // https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L2760-L2773 + key := struct { + N *big.Int + E *big.Int + D *big.Int + Iqmp *big.Int + P *big.Int + Q *big.Int + Comment string + Pad []byte `ssh:"rest"` + }{} + + if err := Unmarshal(pk1.Rest, &key); err != nil { + return nil, err + } - for i, b := range pk1.Pad { - if int(b) != i+1 { - return nil, errors.New("ssh: padding not as expected") + for i, b := range key.Pad { + if int(b) != i+1 { + return nil, errors.New("ssh: padding not as expected") + } } - } - if len(pk1.Priv) != ed25519.PrivateKeySize { - return nil, errors.New("ssh: private key unexpected length") - } + pk := &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: key.N, + E: int(key.E.Int64()), + }, + D: key.D, + Primes: []*big.Int{key.P, key.Q}, + } + + if err := pk.Validate(); err != nil { + return nil, err + } + + pk.Precompute() + + return pk, nil + case KeyAlgoED25519: + key := struct { + Pub []byte + Priv []byte + Comment string + Pad []byte `ssh:"rest"` + }{} + + if err := Unmarshal(pk1.Rest, &key); err != nil { + return nil, err + } + + if len(key.Priv) != ed25519.PrivateKeySize { + return nil, errors.New("ssh: private key unexpected length") + } - pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize)) - copy(pk, pk1.Priv) - return &pk, nil + for i, b := range key.Pad { + if int(b) != i+1 { + return nil, errors.New("ssh: padding not as expected") + } + } + + pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize)) + copy(pk, key.Priv) + return &pk, nil + default: + return nil, errors.New("ssh: unhandled key type") + } } // FingerprintLegacyMD5 returns the user presentation of the key's diff --git a/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go b/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go new file mode 100644 index 00000000..ea92b298 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go @@ -0,0 +1,546 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package knownhosts implements a parser for the OpenSSH +// known_hosts host key database. +package knownhosts + +import ( + "bufio" + "bytes" + "crypto/hmac" + "crypto/rand" + "crypto/sha1" + "encoding/base64" + "errors" + "fmt" + "io" + "net" + "os" + "strings" + + "golang.org/x/crypto/ssh" +) + +// See the sshd manpage +// (http://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT) for +// background. + +type addr struct{ host, port string } + +func (a *addr) String() string { + h := a.host + if strings.Contains(h, ":") { + h = "[" + h + "]" + } + return h + ":" + a.port +} + +type matcher interface { + match([]addr) bool +} + +type hostPattern struct { + negate bool + addr addr +} + +func (p *hostPattern) String() string { + n := "" + if p.negate { + n = "!" + } + + return n + p.addr.String() +} + +type hostPatterns []hostPattern + +func (ps hostPatterns) match(addrs []addr) bool { + matched := false + for _, p := range ps { + for _, a := range addrs { + m := p.match(a) + if !m { + continue + } + if p.negate { + return false + } + matched = true + } + } + return matched +} + +// See +// https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/addrmatch.c +// The matching of * has no regard for separators, unlike filesystem globs +func wildcardMatch(pat []byte, str []byte) bool { + for { + if len(pat) == 0 { + return len(str) == 0 + } + if len(str) == 0 { + return false + } + + if pat[0] == '*' { + if len(pat) == 1 { + return true + } + + for j := range str { + if wildcardMatch(pat[1:], str[j:]) { + return true + } + } + return false + } + + if pat[0] == '?' || pat[0] == str[0] { + pat = pat[1:] + str = str[1:] + } else { + return false + } + } +} + +func (l *hostPattern) match(a addr) bool { + return wildcardMatch([]byte(l.addr.host), []byte(a.host)) && l.addr.port == a.port +} + +type keyDBLine struct { + cert bool + matcher matcher + knownKey KnownKey +} + +func serialize(k ssh.PublicKey) string { + return k.Type() + " " + base64.StdEncoding.EncodeToString(k.Marshal()) +} + +func (l *keyDBLine) match(addrs []addr) bool { + return l.matcher.match(addrs) +} + +type hostKeyDB struct { + // Serialized version of revoked keys + revoked map[string]*KnownKey + lines []keyDBLine +} + +func newHostKeyDB() *hostKeyDB { + db := &hostKeyDB{ + revoked: make(map[string]*KnownKey), + } + + return db +} + +func keyEq(a, b ssh.PublicKey) bool { + return bytes.Equal(a.Marshal(), b.Marshal()) +} + +// IsAuthorityForHost can be used as a callback in ssh.CertChecker +func (db *hostKeyDB) IsHostAuthority(remote ssh.PublicKey, address string) bool { + h, p, err := net.SplitHostPort(address) + if err != nil { + return false + } + a := addr{host: h, port: p} + + for _, l := range db.lines { + if l.cert && keyEq(l.knownKey.Key, remote) && l.match([]addr{a}) { + return true + } + } + return false +} + +// IsRevoked can be used as a callback in ssh.CertChecker +func (db *hostKeyDB) IsRevoked(key *ssh.Certificate) bool { + _, ok := db.revoked[string(key.Marshal())] + return ok +} + +const markerCert = "@cert-authority" +const markerRevoked = "@revoked" + +func nextWord(line []byte) (string, []byte) { + i := bytes.IndexAny(line, "\t ") + if i == -1 { + return string(line), nil + } + + return string(line[:i]), bytes.TrimSpace(line[i:]) +} + +func parseLine(line []byte) (marker, host string, key ssh.PublicKey, err error) { + if w, next := nextWord(line); w == markerCert || w == markerRevoked { + marker = w + line = next + } + + host, line = nextWord(line) + if len(line) == 0 { + return "", "", nil, errors.New("knownhosts: missing host pattern") + } + + // ignore the keytype as it's in the key blob anyway. + _, line = nextWord(line) + if len(line) == 0 { + return "", "", nil, errors.New("knownhosts: missing key type pattern") + } + + keyBlob, _ := nextWord(line) + + keyBytes, err := base64.StdEncoding.DecodeString(keyBlob) + if err != nil { + return "", "", nil, err + } + key, err = ssh.ParsePublicKey(keyBytes) + if err != nil { + return "", "", nil, err + } + + return marker, host, key, nil +} + +func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error { + marker, pattern, key, err := parseLine(line) + if err != nil { + return err + } + + if marker == markerRevoked { + db.revoked[string(key.Marshal())] = &KnownKey{ + Key: key, + Filename: filename, + Line: linenum, + } + + return nil + } + + entry := keyDBLine{ + cert: marker == markerCert, + knownKey: KnownKey{ + Filename: filename, + Line: linenum, + Key: key, + }, + } + + if pattern[0] == '|' { + entry.matcher, err = newHashedHost(pattern) + } else { + entry.matcher, err = newHostnameMatcher(pattern) + } + + if err != nil { + return err + } + + db.lines = append(db.lines, entry) + return nil +} + +func newHostnameMatcher(pattern string) (matcher, error) { + var hps hostPatterns + for _, p := range strings.Split(pattern, ",") { + if len(p) == 0 { + continue + } + + var a addr + var negate bool + if p[0] == '!' { + negate = true + p = p[1:] + } + + if len(p) == 0 { + return nil, errors.New("knownhosts: negation without following hostname") + } + + var err error + if p[0] == '[' { + a.host, a.port, err = net.SplitHostPort(p) + if err != nil { + return nil, err + } + } else { + a.host, a.port, err = net.SplitHostPort(p) + if err != nil { + a.host = p + a.port = "22" + } + } + hps = append(hps, hostPattern{ + negate: negate, + addr: a, + }) + } + return hps, nil +} + +// KnownKey represents a key declared in a known_hosts file. +type KnownKey struct { + Key ssh.PublicKey + Filename string + Line int +} + +func (k *KnownKey) String() string { + return fmt.Sprintf("%s:%d: %s", k.Filename, k.Line, serialize(k.Key)) +} + +// KeyError is returned if we did not find the key in the host key +// database, or there was a mismatch. Typically, in batch +// applications, this should be interpreted as failure. Interactive +// applications can offer an interactive prompt to the user. +type KeyError struct { + // Want holds the accepted host keys. For each key algorithm, + // there can be one hostkey. If Want is empty, the host is + // unknown. If Want is non-empty, there was a mismatch, which + // can signify a MITM attack. + Want []KnownKey +} + +func (u *KeyError) Error() string { + if len(u.Want) == 0 { + return "knownhosts: key is unknown" + } + return "knownhosts: key mismatch" +} + +// RevokedError is returned if we found a key that was revoked. +type RevokedError struct { + Revoked KnownKey +} + +func (r *RevokedError) Error() string { + return "knownhosts: key is revoked" +} + +// check checks a key against the host database. This should not be +// used for verifying certificates. +func (db *hostKeyDB) check(address string, remote net.Addr, remoteKey ssh.PublicKey) error { + if revoked := db.revoked[string(remoteKey.Marshal())]; revoked != nil { + return &RevokedError{Revoked: *revoked} + } + + host, port, err := net.SplitHostPort(remote.String()) + if err != nil { + return fmt.Errorf("knownhosts: SplitHostPort(%s): %v", remote, err) + } + + addrs := []addr{ + {host, port}, + } + + if address != "" { + host, port, err := net.SplitHostPort(address) + if err != nil { + return fmt.Errorf("knownhosts: SplitHostPort(%s): %v", address, err) + } + + addrs = append(addrs, addr{host, port}) + } + + return db.checkAddrs(addrs, remoteKey) +} + +// checkAddrs checks if we can find the given public key for any of +// the given addresses. If we only find an entry for the IP address, +// or only the hostname, then this still succeeds. +func (db *hostKeyDB) checkAddrs(addrs []addr, remoteKey ssh.PublicKey) error { + // TODO(hanwen): are these the right semantics? What if there + // is just a key for the IP address, but not for the + // hostname? + + // Algorithm => key. + knownKeys := map[string]KnownKey{} + for _, l := range db.lines { + if l.match(addrs) { + typ := l.knownKey.Key.Type() + if _, ok := knownKeys[typ]; !ok { + knownKeys[typ] = l.knownKey + } + } + } + + keyErr := &KeyError{} + for _, v := range knownKeys { + keyErr.Want = append(keyErr.Want, v) + } + + // Unknown remote host. + if len(knownKeys) == 0 { + return keyErr + } + + // If the remote host starts using a different, unknown key type, we + // also interpret that as a mismatch. + if known, ok := knownKeys[remoteKey.Type()]; !ok || !keyEq(known.Key, remoteKey) { + return keyErr + } + + return nil +} + +// The Read function parses file contents. +func (db *hostKeyDB) Read(r io.Reader, filename string) error { + scanner := bufio.NewScanner(r) + + lineNum := 0 + for scanner.Scan() { + lineNum++ + line := scanner.Bytes() + line = bytes.TrimSpace(line) + if len(line) == 0 || line[0] == '#' { + continue + } + + if err := db.parseLine(line, filename, lineNum); err != nil { + return fmt.Errorf("knownhosts: %s:%d: %v", filename, lineNum, err) + } + } + return scanner.Err() +} + +// New creates a host key callback from the given OpenSSH host key +// files. The returned callback is for use in +// ssh.ClientConfig.HostKeyCallback. Hashed hostnames are not supported. +func New(files ...string) (ssh.HostKeyCallback, error) { + db := newHostKeyDB() + for _, fn := range files { + f, err := os.Open(fn) + if err != nil { + return nil, err + } + defer f.Close() + if err := db.Read(f, fn); err != nil { + return nil, err + } + } + + var certChecker ssh.CertChecker + certChecker.IsHostAuthority = db.IsHostAuthority + certChecker.IsRevoked = db.IsRevoked + certChecker.HostKeyFallback = db.check + + return certChecker.CheckHostKey, nil +} + +// Normalize normalizes an address into the form used in known_hosts +func Normalize(address string) string { + host, port, err := net.SplitHostPort(address) + if err != nil { + host = address + port = "22" + } + entry := host + if port != "22" { + entry = "[" + entry + "]:" + port + } else if strings.Contains(host, ":") && !strings.HasPrefix(host, "[") { + entry = "[" + entry + "]" + } + return entry +} + +// Line returns a line to add append to the known_hosts files. +func Line(addresses []string, key ssh.PublicKey) string { + var trimmed []string + for _, a := range addresses { + trimmed = append(trimmed, Normalize(a)) + } + + return strings.Join(trimmed, ",") + " " + serialize(key) +} + +// HashHostname hashes the given hostname. The hostname is not +// normalized before hashing. +func HashHostname(hostname string) string { + // TODO(hanwen): check if we can safely normalize this always. + salt := make([]byte, sha1.Size) + + _, err := rand.Read(salt) + if err != nil { + panic(fmt.Sprintf("crypto/rand failure %v", err)) + } + + hash := hashHost(hostname, salt) + return encodeHash(sha1HashType, salt, hash) +} + +func decodeHash(encoded string) (hashType string, salt, hash []byte, err error) { + if len(encoded) == 0 || encoded[0] != '|' { + err = errors.New("knownhosts: hashed host must start with '|'") + return + } + components := strings.Split(encoded, "|") + if len(components) != 4 { + err = fmt.Errorf("knownhosts: got %d components, want 3", len(components)) + return + } + + hashType = components[1] + if salt, err = base64.StdEncoding.DecodeString(components[2]); err != nil { + return + } + if hash, err = base64.StdEncoding.DecodeString(components[3]); err != nil { + return + } + return +} + +func encodeHash(typ string, salt []byte, hash []byte) string { + return strings.Join([]string{"", + typ, + base64.StdEncoding.EncodeToString(salt), + base64.StdEncoding.EncodeToString(hash), + }, "|") +} + +// See https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/hostfile.c#120 +func hashHost(hostname string, salt []byte) []byte { + mac := hmac.New(sha1.New, salt) + mac.Write([]byte(hostname)) + return mac.Sum(nil) +} + +type hashedHost struct { + salt []byte + hash []byte +} + +const sha1HashType = "1" + +func newHashedHost(encoded string) (*hashedHost, error) { + typ, salt, hash, err := decodeHash(encoded) + if err != nil { + return nil, err + } + + // The type field seems for future algorithm agility, but it's + // actually hardcoded in openssh currently, see + // https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/hostfile.c#120 + if typ != sha1HashType { + return nil, fmt.Errorf("knownhosts: got hash type %s, must be '1'", typ) + } + + return &hashedHost{salt: salt, hash: hash}, nil +} + +func (h *hashedHost) match(addrs []addr) bool { + for _, a := range addrs { + if bytes.Equal(hashHost(Normalize(a.String()), h.salt), h.hash) { + return true + } + } + return false +} diff --git a/vendor/golang.org/x/crypto/ssh/mac.go b/vendor/golang.org/x/crypto/ssh/mac.go index 07744ad6..c07a0628 100644 --- a/vendor/golang.org/x/crypto/ssh/mac.go +++ b/vendor/golang.org/x/crypto/ssh/mac.go @@ -15,6 +15,7 @@ import ( type macMode struct { keySize int + etm bool new func(key []byte) hash.Hash } @@ -45,13 +46,16 @@ func (t truncatingMAC) Size() int { func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } var macModes = map[string]*macMode{ - "hmac-sha2-256": {32, func(key []byte) hash.Hash { + "hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash { return hmac.New(sha256.New, key) }}, - "hmac-sha1": {20, func(key []byte) hash.Hash { + "hmac-sha2-256": {32, false, func(key []byte) hash.Hash { + return hmac.New(sha256.New, key) + }}, + "hmac-sha1": {20, false, func(key []byte) hash.Hash { return hmac.New(sha1.New, key) }}, - "hmac-sha1-96": {20, func(key []byte) hash.Hash { + "hmac-sha1-96": {20, false, func(key []byte) hash.Hash { return truncatingMAC{12, hmac.New(sha1.New, key)} }}, } diff --git a/vendor/golang.org/x/crypto/ssh/messages.go b/vendor/golang.org/x/crypto/ssh/messages.go index e6ecd3af..c96e1bec 100644 --- a/vendor/golang.org/x/crypto/ssh/messages.go +++ b/vendor/golang.org/x/crypto/ssh/messages.go @@ -23,10 +23,6 @@ const ( msgUnimplemented = 3 msgDebug = 4 msgNewKeys = 21 - - // Standard authentication messages - msgUserAuthSuccess = 52 - msgUserAuthBanner = 53 ) // SSH messages: @@ -137,6 +133,18 @@ type userAuthFailureMsg struct { PartialSuccess bool } +// See RFC 4252, section 5.1 +const msgUserAuthSuccess = 52 + +// See RFC 4252, section 5.4 +const msgUserAuthBanner = 53 + +type userAuthBannerMsg struct { + Message string `sshtype:"53"` + // unused, but required to allow message parsing + Language string +} + // See RFC 4256, section 3.2 const msgUserAuthInfoRequest = 60 const msgUserAuthInfoResponse = 61 diff --git a/vendor/golang.org/x/crypto/ssh/mux.go b/vendor/golang.org/x/crypto/ssh/mux.go index f3a3ddd7..27a527c1 100644 --- a/vendor/golang.org/x/crypto/ssh/mux.go +++ b/vendor/golang.org/x/crypto/ssh/mux.go @@ -116,9 +116,9 @@ func (m *mux) Wait() error { func newMux(p packetConn) *mux { m := &mux{ conn: p, - incomingChannels: make(chan NewChannel, 16), + incomingChannels: make(chan NewChannel, chanSize), globalResponses: make(chan interface{}, 1), - incomingRequests: make(chan *Request, 16), + incomingRequests: make(chan *Request, chanSize), errCond: newCond(), } if debugMux { diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index 37df1b30..148d2cb2 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -10,26 +10,38 @@ import ( "fmt" "io" "net" + "strings" ) // The Permissions type holds fine-grained permissions that are -// specific to a user or a specific authentication method for a -// user. Permissions, except for "source-address", must be enforced in -// the server application layer, after successful authentication. The -// Permissions are passed on in ServerConn so a server implementation -// can honor them. +// specific to a user or a specific authentication method for a user. +// The Permissions value for a successful authentication attempt is +// available in ServerConn, so it can be used to pass information from +// the user-authentication phase to the application layer. type Permissions struct { - // Critical options restrict default permissions. Common - // restrictions are "source-address" and "force-command". If - // the server cannot enforce the restriction, or does not - // recognize it, the user should not authenticate. + // CriticalOptions indicate restrictions to the default + // permissions, and are typically used in conjunction with + // user certificates. The standard for SSH certificates + // defines "force-command" (only allow the given command to + // execute) and "source-address" (only allow connections from + // the given address). The SSH package currently only enforces + // the "source-address" critical option. It is up to server + // implementations to enforce other critical options, such as + // "force-command", by checking them after the SSH handshake + // is successful. In general, SSH servers should reject + // connections that specify critical options that are unknown + // or not supported. CriticalOptions map[string]string // Extensions are extra functionality that the server may - // offer on authenticated connections. Common extensions are - // "permit-agent-forwarding", "permit-X11-forwarding". Lack of - // support for an extension does not preclude authenticating a - // user. + // offer on authenticated connections. Lack of support for an + // extension does not preclude authenticating a user. Common + // extensions are "permit-agent-forwarding", + // "permit-X11-forwarding". The Go SSH library currently does + // not act on any extension, and it is up to server + // implementations to honor them. Extensions can be used to + // pass data from the authentication callbacks to the server + // application layer. Extensions map[string]string } @@ -44,13 +56,24 @@ type ServerConfig struct { // authenticating. NoClientAuth bool + // MaxAuthTries specifies the maximum number of authentication attempts + // permitted per connection. If set to a negative number, the number of + // attempts are unlimited. If set to zero, the number of attempts are limited + // to 6. + MaxAuthTries int + // PasswordCallback, if non-nil, is called when a user // attempts to authenticate using a password. PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error) - // PublicKeyCallback, if non-nil, is called when a client attempts public - // key authentication. It must return true if the given public key is - // valid for the given user. For example, see CertChecker.Authenticate. + // PublicKeyCallback, if non-nil, is called when a client + // offers a public key for authentication. It must return a nil error + // if the given public key can be used to authenticate the + // given user. For example, see CertChecker.Authenticate. A + // call to this function does not guarantee that the key + // offered is in fact used to authenticate. To record any data + // depending on the public key, store it inside a + // Permissions.Extensions entry. PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) // KeyboardInteractiveCallback, if non-nil, is called when @@ -72,6 +95,10 @@ type ServerConfig struct { // Note that RFC 4253 section 4.2 requires that this string start with // "SSH-2.0-". ServerVersion string + + // BannerCallback, if present, is called and the return string is sent to + // the client after key exchange completed but before authentication. + BannerCallback func(conn ConnMetadata) string } // AddHostKey adds a private key as a host key. If an existing host @@ -142,6 +169,10 @@ type ServerConn struct { func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) { fullConf := *config fullConf.SetDefaults() + if fullConf.MaxAuthTries == 0 { + fullConf.MaxAuthTries = 6 + } + s := &connection{ sshConn: sshConn{conn: c}, } @@ -188,7 +219,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */) s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config) - if err := s.transport.requestInitialKeyChange(); err != nil { + if err := s.transport.waitSession(); err != nil { return nil, err } @@ -231,7 +262,7 @@ func isAcceptableAlgo(algo string) bool { return false } -func checkSourceAddress(addr net.Addr, sourceAddr string) error { +func checkSourceAddress(addr net.Addr, sourceAddrs string) error { if addr == nil { return errors.New("ssh: no address known for client, but source-address match required") } @@ -241,33 +272,71 @@ func checkSourceAddress(addr net.Addr, sourceAddr string) error { return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr) } - if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil { - if bytes.Equal(allowedIP, tcpAddr.IP) { - return nil - } - } else { - _, ipNet, err := net.ParseCIDR(sourceAddr) - if err != nil { - return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err) - } + for _, sourceAddr := range strings.Split(sourceAddrs, ",") { + if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil { + if allowedIP.Equal(tcpAddr.IP) { + return nil + } + } else { + _, ipNet, err := net.ParseCIDR(sourceAddr) + if err != nil { + return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err) + } - if ipNet.Contains(tcpAddr.IP) { - return nil + if ipNet.Contains(tcpAddr.IP) { + return nil + } } } return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr) } +// ServerAuthError implements the error interface. It appends any authentication +// errors that may occur, and is returned if all of the authentication methods +// provided by the user failed to authenticate. +type ServerAuthError struct { + // Errors contains authentication errors returned by the authentication + // callback methods. + Errors []error +} + +func (l ServerAuthError) Error() string { + var errs []string + for _, err := range l.Errors { + errs = append(errs, err.Error()) + } + return "[" + strings.Join(errs, ", ") + "]" +} + func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { - var err error + sessionID := s.transport.getSessionID() var cache pubKeyCache var perms *Permissions + authFailures := 0 + var authErrs []error + userAuthLoop: for { + if authFailures >= config.MaxAuthTries && config.MaxAuthTries > 0 { + discMsg := &disconnectMsg{ + Reason: 2, + Message: "too many authentication failures", + } + + if err := s.transport.writePacket(Marshal(discMsg)); err != nil { + return nil, err + } + + return nil, discMsg + } + var userAuthReq userAuthRequestMsg if packet, err := s.transport.readPacket(); err != nil { + if err == io.EOF { + return nil, &ServerAuthError{Errors: authErrs} + } return nil, err } else if err = Unmarshal(packet, &userAuthReq); err != nil { return nil, err @@ -278,6 +347,19 @@ userAuthLoop: } s.user = userAuthReq.User + + if authFailures == 0 && config.BannerCallback != nil { + msg := config.BannerCallback(s) + if msg != "" { + bannerMsg := &userAuthBannerMsg{ + Message: msg, + } + if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil { + return nil, err + } + } + } + perms = nil authErr := errors.New("no auth passed yet") @@ -286,6 +368,11 @@ userAuthLoop: if config.NoClientAuth { authErr = nil } + + // allow initial attempt of 'none' without penalty + if authFailures == 0 { + authFailures-- + } case "password": if config.PasswordCallback == nil { authErr = errors.New("ssh: password auth not configured") @@ -357,6 +444,7 @@ userAuthLoop: if isQuery { // The client can query if the given public key // would be okay. + if len(payload) > 0 { return nil, parseError(msgUserAuthRequest) } @@ -385,7 +473,7 @@ userAuthLoop: if !isAcceptableAlgo(sig.Format) { break } - signedData := buildDataSignedForAuth(s.transport.getSessionID(), userAuthReq, algoBytes, pubKeyData) + signedData := buildDataSignedForAuth(sessionID, userAuthReq, algoBytes, pubKeyData) if err := pubKey.Verify(signedData, sig); err != nil { return nil, err @@ -398,6 +486,8 @@ userAuthLoop: authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method) } + authErrs = append(authErrs, authErr) + if config.AuthLogCallback != nil { config.AuthLogCallback(s, userAuthReq.Method, authErr) } @@ -406,6 +496,8 @@ userAuthLoop: break userAuthLoop } + authFailures++ + var failureMsg userAuthFailureMsg if config.PasswordCallback != nil { failureMsg.Methods = append(failureMsg.Methods, "password") @@ -421,12 +513,12 @@ userAuthLoop: return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") } - if err = s.transport.writePacket(Marshal(&failureMsg)); err != nil { + if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil { return nil, err } } - if err = s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil { + if err := s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil { return nil, err } return perms, nil diff --git a/vendor/golang.org/x/crypto/ssh/session.go b/vendor/golang.org/x/crypto/ssh/session.go index 17e2aa85..cc06e03f 100644 --- a/vendor/golang.org/x/crypto/ssh/session.go +++ b/vendor/golang.org/x/crypto/ssh/session.go @@ -231,6 +231,26 @@ func (s *Session) RequestSubsystem(subsystem string) error { return err } +// RFC 4254 Section 6.7. +type ptyWindowChangeMsg struct { + Columns uint32 + Rows uint32 + Width uint32 + Height uint32 +} + +// WindowChange informs the remote host about a terminal window dimension change to h rows and w columns. +func (s *Session) WindowChange(h, w int) error { + req := ptyWindowChangeMsg{ + Columns: uint32(w), + Rows: uint32(h), + Width: uint32(w * 8), + Height: uint32(h * 8), + } + _, err := s.ch.SendRequest("window-change", false, Marshal(&req)) + return err +} + // RFC 4254 Section 6.9. type signalMsg struct { Signal string diff --git a/vendor/golang.org/x/crypto/ssh/streamlocal.go b/vendor/golang.org/x/crypto/ssh/streamlocal.go new file mode 100644 index 00000000..a2dccc64 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/streamlocal.go @@ -0,0 +1,115 @@ +package ssh + +import ( + "errors" + "io" + "net" +) + +// streamLocalChannelOpenDirectMsg is a struct used for SSH_MSG_CHANNEL_OPEN message +// with "direct-streamlocal@openssh.com" string. +// +// See openssh-portable/PROTOCOL, section 2.4. connection: Unix domain socket forwarding +// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL#L235 +type streamLocalChannelOpenDirectMsg struct { + socketPath string + reserved0 string + reserved1 uint32 +} + +// forwardedStreamLocalPayload is a struct used for SSH_MSG_CHANNEL_OPEN message +// with "forwarded-streamlocal@openssh.com" string. +type forwardedStreamLocalPayload struct { + SocketPath string + Reserved0 string +} + +// streamLocalChannelForwardMsg is a struct used for SSH2_MSG_GLOBAL_REQUEST message +// with "streamlocal-forward@openssh.com"/"cancel-streamlocal-forward@openssh.com" string. +type streamLocalChannelForwardMsg struct { + socketPath string +} + +// ListenUnix is similar to ListenTCP but uses a Unix domain socket. +func (c *Client) ListenUnix(socketPath string) (net.Listener, error) { + m := streamLocalChannelForwardMsg{ + socketPath, + } + // send message + ok, _, err := c.SendRequest("streamlocal-forward@openssh.com", true, Marshal(&m)) + if err != nil { + return nil, err + } + if !ok { + return nil, errors.New("ssh: streamlocal-forward@openssh.com request denied by peer") + } + ch := c.forwards.add(&net.UnixAddr{Name: socketPath, Net: "unix"}) + + return &unixListener{socketPath, c, ch}, nil +} + +func (c *Client) dialStreamLocal(socketPath string) (Channel, error) { + msg := streamLocalChannelOpenDirectMsg{ + socketPath: socketPath, + } + ch, in, err := c.OpenChannel("direct-streamlocal@openssh.com", Marshal(&msg)) + if err != nil { + return nil, err + } + go DiscardRequests(in) + return ch, err +} + +type unixListener struct { + socketPath string + + conn *Client + in <-chan forward +} + +// Accept waits for and returns the next connection to the listener. +func (l *unixListener) Accept() (net.Conn, error) { + s, ok := <-l.in + if !ok { + return nil, io.EOF + } + ch, incoming, err := s.newCh.Accept() + if err != nil { + return nil, err + } + go DiscardRequests(incoming) + + return &chanConn{ + Channel: ch, + laddr: &net.UnixAddr{ + Name: l.socketPath, + Net: "unix", + }, + raddr: &net.UnixAddr{ + Name: "@", + Net: "unix", + }, + }, nil +} + +// Close closes the listener. +func (l *unixListener) Close() error { + // this also closes the listener. + l.conn.forwards.remove(&net.UnixAddr{Name: l.socketPath, Net: "unix"}) + m := streamLocalChannelForwardMsg{ + l.socketPath, + } + ok, _, err := l.conn.SendRequest("cancel-streamlocal-forward@openssh.com", true, Marshal(&m)) + if err == nil && !ok { + err = errors.New("ssh: cancel-streamlocal-forward@openssh.com failed") + } + return err +} + +// Addr returns the listener's network address. +func (l *unixListener) Addr() net.Addr { + return &net.UnixAddr{ + Name: l.socketPath, + Net: "unix", + } +} diff --git a/vendor/golang.org/x/crypto/ssh/tcpip.go b/vendor/golang.org/x/crypto/ssh/tcpip.go index 6151241f..acf17175 100644 --- a/vendor/golang.org/x/crypto/ssh/tcpip.go +++ b/vendor/golang.org/x/crypto/ssh/tcpip.go @@ -20,12 +20,20 @@ import ( // addr. Incoming connections will be available by calling Accept on // the returned net.Listener. The listener must be serviced, or the // SSH connection may hang. +// N must be "tcp", "tcp4", "tcp6", or "unix". func (c *Client) Listen(n, addr string) (net.Listener, error) { - laddr, err := net.ResolveTCPAddr(n, addr) - if err != nil { - return nil, err + switch n { + case "tcp", "tcp4", "tcp6": + laddr, err := net.ResolveTCPAddr(n, addr) + if err != nil { + return nil, err + } + return c.ListenTCP(laddr) + case "unix": + return c.ListenUnix(addr) + default: + return nil, fmt.Errorf("ssh: unsupported protocol: %s", n) } - return c.ListenTCP(laddr) } // Automatic port allocation is broken with OpenSSH before 6.0. See @@ -116,7 +124,7 @@ func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { } // Register this forward, using the port number we obtained. - ch := c.forwards.add(*laddr) + ch := c.forwards.add(laddr) return &tcpListener{laddr, c, ch}, nil } @@ -131,7 +139,7 @@ type forwardList struct { // forwardEntry represents an established mapping of a laddr on a // remote ssh server to a channel connected to a tcpListener. type forwardEntry struct { - laddr net.TCPAddr + laddr net.Addr c chan forward } @@ -139,16 +147,16 @@ type forwardEntry struct { // arguments to add/remove/lookup should be address as specified in // the original forward-request. type forward struct { - newCh NewChannel // the ssh client channel underlying this forward - raddr *net.TCPAddr // the raddr of the incoming connection + newCh NewChannel // the ssh client channel underlying this forward + raddr net.Addr // the raddr of the incoming connection } -func (l *forwardList) add(addr net.TCPAddr) chan forward { +func (l *forwardList) add(addr net.Addr) chan forward { l.Lock() defer l.Unlock() f := forwardEntry{ - addr, - make(chan forward, 1), + laddr: addr, + c: make(chan forward, 1), } l.entries = append(l.entries, f) return f.c @@ -176,44 +184,69 @@ func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) { func (l *forwardList) handleChannels(in <-chan NewChannel) { for ch := range in { - var payload forwardedTCPPayload - if err := Unmarshal(ch.ExtraData(), &payload); err != nil { - ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error()) - continue + var ( + laddr net.Addr + raddr net.Addr + err error + ) + switch channelType := ch.ChannelType(); channelType { + case "forwarded-tcpip": + var payload forwardedTCPPayload + if err = Unmarshal(ch.ExtraData(), &payload); err != nil { + ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error()) + continue + } + + // RFC 4254 section 7.2 specifies that incoming + // addresses should list the address, in string + // format. It is implied that this should be an IP + // address, as it would be impossible to connect to it + // otherwise. + laddr, err = parseTCPAddr(payload.Addr, payload.Port) + if err != nil { + ch.Reject(ConnectionFailed, err.Error()) + continue + } + raddr, err = parseTCPAddr(payload.OriginAddr, payload.OriginPort) + if err != nil { + ch.Reject(ConnectionFailed, err.Error()) + continue + } + + case "forwarded-streamlocal@openssh.com": + var payload forwardedStreamLocalPayload + if err = Unmarshal(ch.ExtraData(), &payload); err != nil { + ch.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+err.Error()) + continue + } + laddr = &net.UnixAddr{ + Name: payload.SocketPath, + Net: "unix", + } + raddr = &net.UnixAddr{ + Name: "@", + Net: "unix", + } + default: + panic(fmt.Errorf("ssh: unknown channel type %s", channelType)) } - - // RFC 4254 section 7.2 specifies that incoming - // addresses should list the address, in string - // format. It is implied that this should be an IP - // address, as it would be impossible to connect to it - // otherwise. - laddr, err := parseTCPAddr(payload.Addr, payload.Port) - if err != nil { - ch.Reject(ConnectionFailed, err.Error()) - continue - } - raddr, err := parseTCPAddr(payload.OriginAddr, payload.OriginPort) - if err != nil { - ch.Reject(ConnectionFailed, err.Error()) - continue - } - - if ok := l.forward(*laddr, *raddr, ch); !ok { + if ok := l.forward(laddr, raddr, ch); !ok { // Section 7.2, implementations MUST reject spurious incoming // connections. ch.Reject(Prohibited, "no forward for address") continue } + } } // remove removes the forward entry, and the channel feeding its // listener. -func (l *forwardList) remove(addr net.TCPAddr) { +func (l *forwardList) remove(addr net.Addr) { l.Lock() defer l.Unlock() for i, f := range l.entries { - if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port { + if addr.Network() == f.laddr.Network() && addr.String() == f.laddr.String() { l.entries = append(l.entries[:i], l.entries[i+1:]...) close(f.c) return @@ -231,12 +264,12 @@ func (l *forwardList) closeAll() { l.entries = nil } -func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool { +func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool { l.Lock() defer l.Unlock() for _, f := range l.entries { - if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port { - f.c <- forward{ch, &raddr} + if laddr.Network() == f.laddr.Network() && laddr.String() == f.laddr.String() { + f.c <- forward{newCh: ch, raddr: raddr} return true } } @@ -262,7 +295,7 @@ func (l *tcpListener) Accept() (net.Conn, error) { } go DiscardRequests(incoming) - return &tcpChanConn{ + return &chanConn{ Channel: ch, laddr: l.laddr, raddr: s.raddr, @@ -277,7 +310,7 @@ func (l *tcpListener) Close() error { } // this also closes the listener. - l.conn.forwards.remove(*l.laddr) + l.conn.forwards.remove(l.laddr) ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m)) if err == nil && !ok { err = errors.New("ssh: cancel-tcpip-forward failed") @@ -293,29 +326,52 @@ func (l *tcpListener) Addr() net.Addr { // Dial initiates a connection to the addr from the remote host. // The resulting connection has a zero LocalAddr() and RemoteAddr(). func (c *Client) Dial(n, addr string) (net.Conn, error) { - // Parse the address into host and numeric port. - host, portString, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } - port, err := strconv.ParseUint(portString, 10, 16) - if err != nil { - return nil, err - } - // Use a zero address for local and remote address. - zeroAddr := &net.TCPAddr{ - IP: net.IPv4zero, - Port: 0, - } - ch, err := c.dial(net.IPv4zero.String(), 0, host, int(port)) - if err != nil { - return nil, err + var ch Channel + switch n { + case "tcp", "tcp4", "tcp6": + // Parse the address into host and numeric port. + host, portString, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + port, err := strconv.ParseUint(portString, 10, 16) + if err != nil { + return nil, err + } + ch, err = c.dial(net.IPv4zero.String(), 0, host, int(port)) + if err != nil { + return nil, err + } + // Use a zero address for local and remote address. + zeroAddr := &net.TCPAddr{ + IP: net.IPv4zero, + Port: 0, + } + return &chanConn{ + Channel: ch, + laddr: zeroAddr, + raddr: zeroAddr, + }, nil + case "unix": + var err error + ch, err = c.dialStreamLocal(addr) + if err != nil { + return nil, err + } + return &chanConn{ + Channel: ch, + laddr: &net.UnixAddr{ + Name: "@", + Net: "unix", + }, + raddr: &net.UnixAddr{ + Name: addr, + Net: "unix", + }, + }, nil + default: + return nil, fmt.Errorf("ssh: unsupported protocol: %s", n) } - return &tcpChanConn{ - Channel: ch, - laddr: zeroAddr, - raddr: zeroAddr, - }, nil } // DialTCP connects to the remote address raddr on the network net, @@ -332,7 +388,7 @@ func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) if err != nil { return nil, err } - return &tcpChanConn{ + return &chanConn{ Channel: ch, laddr: laddr, raddr: raddr, @@ -366,26 +422,26 @@ type tcpChan struct { Channel // the backing channel } -// tcpChanConn fulfills the net.Conn interface without +// chanConn fulfills the net.Conn interface without // the tcpChan having to hold laddr or raddr directly. -type tcpChanConn struct { +type chanConn struct { Channel laddr, raddr net.Addr } // LocalAddr returns the local network address. -func (t *tcpChanConn) LocalAddr() net.Addr { +func (t *chanConn) LocalAddr() net.Addr { return t.laddr } // RemoteAddr returns the remote network address. -func (t *tcpChanConn) RemoteAddr() net.Addr { +func (t *chanConn) RemoteAddr() net.Addr { return t.raddr } // SetDeadline sets the read and write deadlines associated // with the connection. -func (t *tcpChanConn) SetDeadline(deadline time.Time) error { +func (t *chanConn) SetDeadline(deadline time.Time) error { if err := t.SetReadDeadline(deadline); err != nil { return err } @@ -396,12 +452,14 @@ func (t *tcpChanConn) SetDeadline(deadline time.Time) error { // A zero value for t means Read will not time out. // After the deadline, the error from Read will implement net.Error // with Timeout() == true. -func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error { +func (t *chanConn) SetReadDeadline(deadline time.Time) error { + // for compatibility with previous version, + // the error message contains "tcpChan" return errors.New("ssh: tcpChan: deadline not supported") } // SetWriteDeadline exists to satisfy the net.Conn interface // but is not implemented by this type. It always returns an error. -func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error { +func (t *chanConn) SetWriteDeadline(deadline time.Time) error { return errors.New("ssh: tcpChan: deadline not supported") } diff --git a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go index 741eeb13..18379a93 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go @@ -132,8 +132,11 @@ const ( keyPasteEnd ) -var pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'} -var pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'} +var ( + crlf = []byte{'\r', '\n'} + pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'} + pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'} +) // bytesToKey tries to parse a key sequence from b. If successful, it returns // the key and the remainder of the input. Otherwise it returns utf8.RuneError. @@ -333,7 +336,7 @@ func (t *Terminal) advanceCursor(places int) { // So, if we are stopping at the end of a line, we // need to write a newline so that our cursor can be // advanced to the next line. - t.outBuf = append(t.outBuf, '\n') + t.outBuf = append(t.outBuf, '\r', '\n') } } @@ -593,6 +596,35 @@ func (t *Terminal) writeLine(line []rune) { } } +// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n. +func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) { + for len(buf) > 0 { + i := bytes.IndexByte(buf, '\n') + todo := len(buf) + if i >= 0 { + todo = i + } + + var nn int + nn, err = w.Write(buf[:todo]) + n += nn + if err != nil { + return n, err + } + buf = buf[todo:] + + if i >= 0 { + if _, err = w.Write(crlf); err != nil { + return n, err + } + n += 1 + buf = buf[1:] + } + } + + return n, nil +} + func (t *Terminal) Write(buf []byte) (n int, err error) { t.lock.Lock() defer t.lock.Unlock() @@ -600,7 +632,7 @@ func (t *Terminal) Write(buf []byte) (n int, err error) { if t.cursorX == 0 && t.cursorY == 0 { // This is the easy case: there's nothing on the screen that we // have to move out of the way. - return t.c.Write(buf) + return writeWithCRLF(t.c, buf) } // We have a prompt and possibly user input on the screen. We @@ -620,7 +652,7 @@ func (t *Terminal) Write(buf []byte) (n int, err error) { } t.outBuf = t.outBuf[:0] - if n, err = t.c.Write(buf); err != nil { + if n, err = writeWithCRLF(t.c, buf); err != nil { return } @@ -740,8 +772,6 @@ func (t *Terminal) readLine() (line string, err error) { t.remainder = t.inBuf[:n+len(t.remainder)] } - - panic("unreachable") // for Go 1.0. } // SetPrompt sets the prompt to be used when reading subsequent lines. @@ -890,3 +920,32 @@ func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { } return s.entries[index], true } + +// readPasswordLine reads from reader until it finds \n or io.EOF. +// The slice returned does not include the \n. +// readPasswordLine also ignores any \r it finds. +func readPasswordLine(reader io.Reader) ([]byte, error) { + var buf [1]byte + var ret []byte + + for { + n, err := reader.Read(buf[:]) + if n > 0 { + switch buf[0] { + case '\n': + return ret, nil + case '\r': + // remove \r from passwords on Windows + default: + ret = append(ret, buf[0]) + } + continue + } + if err != nil { + if err == io.EOF && len(ret) > 0 { + return ret, nil + } + return ret, err + } + } +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util.go b/vendor/golang.org/x/crypto/ssh/terminal/util.go index c869213e..02dad484 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util.go @@ -17,41 +17,41 @@ package terminal // import "golang.org/x/crypto/ssh/terminal" import ( - "io" - "syscall" - "unsafe" + "golang.org/x/sys/unix" ) // State contains the state of a terminal. type State struct { - termios syscall.Termios + termios unix.Termios } // IsTerminal returns true if the given file descriptor is a terminal. func IsTerminal(fd int) bool { - var termios syscall.Termios - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) - return err == 0 + _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + return err == nil } // MakeRaw put the terminal connected to the given file descriptor into raw // mode and returns the previous state of the terminal so that it can be // restored. func MakeRaw(fd int) (*State, error) { - var oldState State - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { + termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + if err != nil { return nil, err } - newState := oldState.termios + oldState := State{termios: *termios} + // This attempts to replicate the behaviour documented for cfmakeraw in // the termios(3) manpage. - newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON - newState.Oflag &^= syscall.OPOST - newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN - newState.Cflag &^= syscall.CSIZE | syscall.PARENB - newState.Cflag |= syscall.CS8 - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { + termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON + termios.Oflag &^= unix.OPOST + termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN + termios.Cflag &^= unix.CSIZE | unix.PARENB + termios.Cflag |= unix.CS8 + termios.Cc[unix.VMIN] = 1 + termios.Cc[unix.VTIME] = 0 + if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil { return nil, err } @@ -61,73 +61,56 @@ func MakeRaw(fd int) (*State, error) { // GetState returns the current state of a terminal which may be useful to // restore the terminal after a signal. func GetState(fd int) (*State, error) { - var oldState State - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { + termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + if err != nil { return nil, err } - return &oldState, nil + return &State{termios: *termios}, nil } // Restore restores the terminal connected to the given file descriptor to a // previous state. func Restore(fd int, state *State) error { - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) - return err + return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios) } // GetSize returns the dimensions of the given terminal. func GetSize(fd int) (width, height int, err error) { - var dimensions [4]uint16 - - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 { + ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) + if err != nil { return -1, -1, err } - return int(dimensions[1]), int(dimensions[0]), nil + return int(ws.Col), int(ws.Row), nil +} + +// passwordReader is an io.Reader that reads from a specific file descriptor. +type passwordReader int + +func (r passwordReader) Read(buf []byte) (int, error) { + return unix.Read(int(r), buf) } // ReadPassword reads a line of input from a terminal without local echo. This // is commonly used for inputting passwords and other sensitive data. The slice // returned does not include the \n. func ReadPassword(fd int) ([]byte, error) { - var oldState syscall.Termios - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 { + termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + if err != nil { return nil, err } - newState := oldState - newState.Lflag &^= syscall.ECHO - newState.Lflag |= syscall.ICANON | syscall.ISIG - newState.Iflag |= syscall.ICRNL - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { + newState := *termios + newState.Lflag &^= unix.ECHO + newState.Lflag |= unix.ICANON | unix.ISIG + newState.Iflag |= unix.ICRNL + if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil { return nil, err } defer func() { - syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0) + unix.IoctlSetTermios(fd, ioctlWriteTermios, termios) }() - var buf [16]byte - var ret []byte - for { - n, err := syscall.Read(fd, buf[:]) - if err != nil { - return nil, err - } - if n == 0 { - if len(ret) == 0 { - return nil, io.EOF - } - break - } - if buf[n-1] == '\n' { - n-- - } - ret = append(ret, buf[:n]...) - if n < len(buf) { - break - } - } - - return ret, nil + return readPasswordLine(passwordReader(fd)) } diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go b/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go index 9c1ffd14..cb23a590 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go @@ -6,7 +6,7 @@ package terminal -import "syscall" +import "golang.org/x/sys/unix" -const ioctlReadTermios = syscall.TIOCGETA -const ioctlWriteTermios = syscall.TIOCSETA +const ioctlReadTermios = unix.TIOCGETA +const ioctlWriteTermios = unix.TIOCSETA diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go b/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go index 5883b22d..5fadfe8a 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go @@ -4,8 +4,7 @@ package terminal -// These constants are declared here, rather than importing -// them from the syscall package as some syscall packages, even -// on linux, for example gccgo, do not declare them. -const ioctlReadTermios = 0x5401 // syscall.TCGETS -const ioctlWriteTermios = 0x5402 // syscall.TCSETS +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TCGETS +const ioctlWriteTermios = unix.TCSETS diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go index 07eb5edd..a2e1b57d 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go @@ -14,14 +14,12 @@ import ( // State contains the state of a terminal. type State struct { - termios syscall.Termios + state *unix.Termios } // IsTerminal returns true if the given file descriptor is a terminal. func IsTerminal(fd int) bool { - // see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c - var termio unix.Termio - err := unix.IoctlSetTermio(fd, unix.TCGETA, &termio) + _, err := unix.IoctlGetTermio(fd, unix.TCGETA) return err == nil } @@ -71,3 +69,60 @@ func ReadPassword(fd int) ([]byte, error) { return ret, nil } + +// MakeRaw puts the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +// see http://cr.illumos.org/~webrev/andy_js/1060/ +func MakeRaw(fd int) (*State, error) { + oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS) + if err != nil { + return nil, err + } + oldTermios := *oldTermiosPtr + + newTermios := oldTermios + newTermios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON + newTermios.Oflag &^= syscall.OPOST + newTermios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN + newTermios.Cflag &^= syscall.CSIZE | syscall.PARENB + newTermios.Cflag |= syscall.CS8 + newTermios.Cc[unix.VMIN] = 1 + newTermios.Cc[unix.VTIME] = 0 + + if err := unix.IoctlSetTermios(fd, unix.TCSETS, &newTermios); err != nil { + return nil, err + } + + return &State{ + state: oldTermiosPtr, + }, nil +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, oldState *State) error { + return unix.IoctlSetTermios(fd, unix.TCSETS, oldState.state) +} + +// GetState returns the current state of a terminal which may be useful to +// restore the terminal after a signal. +func GetState(fd int) (*State, error) { + oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS) + if err != nil { + return nil, err + } + + return &State{ + state: oldTermiosPtr, + }, nil +} + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (width, height int, err error) { + ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) + if err != nil { + return 0, 0, err + } + return int(ws.Col), int(ws.Row), nil +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go index ae9fa9ec..60979ccd 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go @@ -17,54 +17,7 @@ package terminal import ( - "io" - "syscall" - "unsafe" -) - -const ( - enableLineInput = 2 - enableEchoInput = 4 - enableProcessedInput = 1 - enableWindowInput = 8 - enableMouseInput = 16 - enableInsertMode = 32 - enableQuickEditMode = 64 - enableExtendedFlags = 128 - enableAutoPosition = 256 - enableProcessedOutput = 1 - enableWrapAtEolOutput = 2 -) - -var kernel32 = syscall.NewLazyDLL("kernel32.dll") - -var ( - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") - procSetConsoleMode = kernel32.NewProc("SetConsoleMode") - procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") -) - -type ( - short int16 - word uint16 - - coord struct { - x short - y short - } - smallRect struct { - left short - top short - right short - bottom short - } - consoleScreenBufferInfo struct { - size coord - cursorPosition coord - attributes word - window smallRect - maximumWindowSize coord - } + "golang.org/x/sys/windows" ) type State struct { @@ -74,8 +27,8 @@ type State struct { // IsTerminal returns true if the given file descriptor is a terminal. func IsTerminal(fd int) bool { var st uint32 - r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) - return r != 0 && e == 0 + err := windows.GetConsoleMode(windows.Handle(fd), &st) + return err == nil } // MakeRaw put the terminal connected to the given file descriptor into raw @@ -83,14 +36,12 @@ func IsTerminal(fd int) bool { // restored. func MakeRaw(fd int) (*State, error) { var st uint32 - _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) - if e != 0 { - return nil, error(e) + if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { + return nil, err } - raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput) - _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0) - if e != 0 { - return nil, error(e) + raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT) + if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil { + return nil, err } return &State{st}, nil } @@ -99,9 +50,8 @@ func MakeRaw(fd int) (*State, error) { // restore the terminal after a signal. func GetState(fd int) (*State, error) { var st uint32 - _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) - if e != 0 { - return nil, error(e) + if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { + return nil, err } return &State{st}, nil } @@ -109,18 +59,23 @@ func GetState(fd int) (*State, error) { // Restore restores the terminal connected to the given file descriptor to a // previous state. func Restore(fd int, state *State) error { - _, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0) - return err + return windows.SetConsoleMode(windows.Handle(fd), state.mode) } // GetSize returns the dimensions of the given terminal. func GetSize(fd int) (width, height int, err error) { - var info consoleScreenBufferInfo - _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0) - if e != 0 { - return 0, 0, error(e) + var info windows.ConsoleScreenBufferInfo + if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { + return 0, 0, err } - return int(info.size.x), int(info.size.y), nil + return int(info.Size.X), int(info.Size.Y), nil +} + +// passwordReader is an io.Reader that reads from a specific Windows HANDLE. +type passwordReader int + +func (r passwordReader) Read(buf []byte) (int, error) { + return windows.Read(windows.Handle(r), buf) } // ReadPassword reads a line of input from a terminal without local echo. This @@ -128,47 +83,20 @@ func GetSize(fd int) (width, height int, err error) { // returned does not include the \n. func ReadPassword(fd int) ([]byte, error) { var st uint32 - _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) - if e != 0 { - return nil, error(e) + if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { + return nil, err } old := st - st &^= (enableEchoInput) - st |= (enableProcessedInput | enableLineInput | enableProcessedOutput) - _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0) - if e != 0 { - return nil, error(e) + st &^= (windows.ENABLE_ECHO_INPUT) + st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT) + if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil { + return nil, err } defer func() { - syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0) + windows.SetConsoleMode(windows.Handle(fd), old) }() - var buf [16]byte - var ret []byte - for { - n, err := syscall.Read(syscall.Handle(fd), buf[:]) - if err != nil { - return nil, err - } - if n == 0 { - if len(ret) == 0 { - return nil, io.EOF - } - break - } - if buf[n-1] == '\n' { - n-- - } - if n > 0 && buf[n-1] == '\r' { - n-- - } - ret = append(ret, buf[:n]...) - if n < len(buf) { - break - } - } - - return ret, nil + return readPasswordLine(passwordReader(fd)) } diff --git a/vendor/golang.org/x/crypto/ssh/transport.go b/vendor/golang.org/x/crypto/ssh/transport.go index 62fba629..ab2b8876 100644 --- a/vendor/golang.org/x/crypto/ssh/transport.go +++ b/vendor/golang.org/x/crypto/ssh/transport.go @@ -8,8 +8,13 @@ import ( "bufio" "errors" "io" + "log" ) +// debugTransport if set, will print packet types as they go over the +// wire. No message decoding is done, to minimize the impact on timing. +const debugTransport = false + const ( gcmCipherID = "aes128-gcm@openssh.com" aes128cbcID = "aes128-cbc" @@ -22,7 +27,9 @@ type packetConn interface { // Encrypt and send a packet of data to the remote peer. writePacket(packet []byte) error - // Read a packet from the connection + // Read a packet from the connection. The read is blocking, + // i.e. if error is nil, then the returned byte slice is + // always non-empty. readPacket() ([]byte, error) // Close closes the write-side of the connection. @@ -38,7 +45,7 @@ type transport struct { bufReader *bufio.Reader bufWriter *bufio.Writer rand io.Reader - + isClient bool io.Closer } @@ -84,9 +91,38 @@ func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) err return nil } +func (t *transport) printPacket(p []byte, write bool) { + if len(p) == 0 { + return + } + who := "server" + if t.isClient { + who = "client" + } + what := "read" + if write { + what = "write" + } + + log.Println(what, who, p[0]) +} + // Read and decrypt next packet. -func (t *transport) readPacket() ([]byte, error) { - return t.reader.readPacket(t.bufReader) +func (t *transport) readPacket() (p []byte, err error) { + for { + p, err = t.reader.readPacket(t.bufReader) + if err != nil { + break + } + if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) { + break + } + } + if debugTransport { + t.printPacket(p, false) + } + + return p, err } func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { @@ -129,6 +165,9 @@ func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { } func (t *transport) writePacket(packet []byte) error { + if debugTransport { + t.printPacket(packet, true) + } return t.writer.writePacket(t.bufWriter, t.rand, packet) } @@ -169,6 +208,8 @@ func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transp }, Closer: rwc, } + t.isClient = isClient + if isClient { t.reader.dir = serverKeys t.writer.dir = clientKeys @@ -213,7 +254,7 @@ func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (pac iv, key, macKey := generateKeys(d, algs, kex) if algs.Cipher == gcmCipherID { - return newGCMCipher(iv, key, macKey) + return newGCMCipher(iv, key) } if algs.Cipher == aes128cbcID { @@ -226,6 +267,7 @@ func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (pac c := &streamPacketCipher{ mac: macModes[algs.MAC].new(macKey), + etm: macModes[algs.MAC].etm, } c.macResult = make([]byte, c.mac.Size()) diff --git a/vendor/golang.org/x/net/http2/h2demo/h2demo.go b/vendor/golang.org/x/net/http2/h2demo/h2demo.go new file mode 100644 index 00000000..980b6d67 --- /dev/null +++ b/vendor/golang.org/x/net/http2/h2demo/h2demo.go @@ -0,0 +1,486 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build h2demo + +package main + +import ( + "bytes" + "crypto/tls" + "flag" + "fmt" + "hash/crc32" + "image" + "image/jpeg" + "io" + "io/ioutil" + "log" + "net" + "net/http" + "os" + "path" + "regexp" + "runtime" + "strconv" + "strings" + "sync" + "time" + + "go4.org/syncutil/singleflight" + "golang.org/x/crypto/acme/autocert" + "golang.org/x/net/http2" +) + +var ( + prod = flag.Bool("prod", false, "Whether to configure itself to be the production http2.golang.org server.") + + httpsAddr = flag.String("https_addr", "localhost:4430", "TLS address to listen on ('host:port' or ':port'). Required.") + httpAddr = flag.String("http_addr", "", "Plain HTTP address to listen on ('host:port', or ':port'). Empty means no HTTP.") + + hostHTTP = flag.String("http_host", "", "Optional host or host:port to use for http:// links to this service. By default, this is implied from -http_addr.") + hostHTTPS = flag.String("https_host", "", "Optional host or host:port to use for http:// links to this service. By default, this is implied from -https_addr.") +) + +func homeOldHTTP(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, ` + +

Go + HTTP/2

+

Welcome to the Go language's HTTP/2 demo & interop server.

+

Unfortunately, you're not using HTTP/2 right now. To do so:

+
    +
  • Use Firefox Nightly or go to about:config and enable "network.http.spdy.enabled.http2draft"
  • +
  • Use Google Chrome Canary and/or go to chrome://flags/#enable-spdy4 to Enable SPDY/4 (Chrome's name for HTTP/2)
  • +
+

See code & instructions for connecting at https://github.com/golang/net/tree/master/http2.

+ +`) +} + +func home(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + io.WriteString(w, ` + +

Go + HTTP/2

+ +

Welcome to the Go language's HTTP/2 demo & interop server.

+ +

Congratulations, you're using HTTP/2 right now.

+ +

This server exists for others in the HTTP/2 community to test their HTTP/2 client implementations and point out flaws in our server.

+ +

+The code is at golang.org/x/net/http2 and +is used transparently by the Go standard library from Go 1.6 and later. +

+ +

Contact info: bradfitz@golang.org, or file a bug.

+ +

Handlers for testing

+
    +
  • GET /reqinfo to dump the request + headers received
  • +
  • GET /clockstream streams the current time every second
  • +
  • GET /gophertiles to see a page with a bunch of images
  • +
  • GET /file/gopher.png for a small file (does If-Modified-Since, Content-Range, etc)
  • +
  • GET /file/go.src.tar.gz for a larger file (~10 MB)
  • +
  • GET /redirect to redirect back to / (this page)
  • +
  • GET /goroutines to see all active goroutines in this server
  • +
  • PUT something to /crc32 to get a count of number of bytes and its CRC-32
  • +
  • PUT something to /ECHO and it will be streamed back to you capitalized
  • +
+ +`) +} + +func reqInfoHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + fmt.Fprintf(w, "Method: %s\n", r.Method) + fmt.Fprintf(w, "Protocol: %s\n", r.Proto) + fmt.Fprintf(w, "Host: %s\n", r.Host) + fmt.Fprintf(w, "RemoteAddr: %s\n", r.RemoteAddr) + fmt.Fprintf(w, "RequestURI: %q\n", r.RequestURI) + fmt.Fprintf(w, "URL: %#v\n", r.URL) + fmt.Fprintf(w, "Body.ContentLength: %d (-1 means unknown)\n", r.ContentLength) + fmt.Fprintf(w, "Close: %v (relevant for HTTP/1 only)\n", r.Close) + fmt.Fprintf(w, "TLS: %#v\n", r.TLS) + fmt.Fprintf(w, "\nHeaders:\n") + r.Header.Write(w) +} + +func crcHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != "PUT" { + http.Error(w, "PUT required.", 400) + return + } + crc := crc32.NewIEEE() + n, err := io.Copy(crc, r.Body) + if err == nil { + w.Header().Set("Content-Type", "text/plain") + fmt.Fprintf(w, "bytes=%d, CRC32=%x", n, crc.Sum(nil)) + } +} + +type capitalizeReader struct { + r io.Reader +} + +func (cr capitalizeReader) Read(p []byte) (n int, err error) { + n, err = cr.r.Read(p) + for i, b := range p[:n] { + if b >= 'a' && b <= 'z' { + p[i] = b - ('a' - 'A') + } + } + return +} + +type flushWriter struct { + w io.Writer +} + +func (fw flushWriter) Write(p []byte) (n int, err error) { + n, err = fw.w.Write(p) + if f, ok := fw.w.(http.Flusher); ok { + f.Flush() + } + return +} + +func echoCapitalHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != "PUT" { + http.Error(w, "PUT required.", 400) + return + } + io.Copy(flushWriter{w}, capitalizeReader{r.Body}) +} + +var ( + fsGrp singleflight.Group + fsMu sync.Mutex // guards fsCache + fsCache = map[string]http.Handler{} +) + +// fileServer returns a file-serving handler that proxies URL. +// It lazily fetches URL on the first access and caches its contents forever. +func fileServer(url string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + hi, err := fsGrp.Do(url, func() (interface{}, error) { + fsMu.Lock() + if h, ok := fsCache[url]; ok { + fsMu.Unlock() + return h, nil + } + fsMu.Unlock() + + res, err := http.Get(url) + if err != nil { + return nil, err + } + defer res.Body.Close() + slurp, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + + modTime := time.Now() + var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.ServeContent(w, r, path.Base(url), modTime, bytes.NewReader(slurp)) + }) + fsMu.Lock() + fsCache[url] = h + fsMu.Unlock() + return h, nil + }) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + hi.(http.Handler).ServeHTTP(w, r) + }) +} + +func clockStreamHandler(w http.ResponseWriter, r *http.Request) { + clientGone := w.(http.CloseNotifier).CloseNotify() + w.Header().Set("Content-Type", "text/plain") + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + fmt.Fprintf(w, "# ~1KB of junk to force browsers to start rendering immediately: \n") + io.WriteString(w, strings.Repeat("# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", 13)) + + for { + fmt.Fprintf(w, "%v\n", time.Now()) + w.(http.Flusher).Flush() + select { + case <-ticker.C: + case <-clientGone: + log.Printf("Client %v disconnected from the clock", r.RemoteAddr) + return + } + } +} + +func registerHandlers() { + tiles := newGopherTilesHandler() + + mux2 := http.NewServeMux() + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if r.TLS == nil { + if r.URL.Path == "/gophertiles" { + tiles.ServeHTTP(w, r) + return + } + http.Redirect(w, r, "https://"+httpsHost()+"/", http.StatusFound) + return + } + if r.ProtoMajor == 1 { + if r.URL.Path == "/reqinfo" { + reqInfoHandler(w, r) + return + } + homeOldHTTP(w, r) + return + } + mux2.ServeHTTP(w, r) + }) + mux2.HandleFunc("/", home) + mux2.Handle("/file/gopher.png", fileServer("https://golang.org/doc/gopher/frontpage.png")) + mux2.Handle("/file/go.src.tar.gz", fileServer("https://storage.googleapis.com/golang/go1.4.1.src.tar.gz")) + mux2.HandleFunc("/reqinfo", reqInfoHandler) + mux2.HandleFunc("/crc32", crcHandler) + mux2.HandleFunc("/ECHO", echoCapitalHandler) + mux2.HandleFunc("/clockstream", clockStreamHandler) + mux2.Handle("/gophertiles", tiles) + mux2.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, "/", http.StatusFound) + }) + stripHomedir := regexp.MustCompile(`/(Users|home)/\w+`) + mux2.HandleFunc("/goroutines", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + buf := make([]byte, 2<<20) + w.Write(stripHomedir.ReplaceAll(buf[:runtime.Stack(buf, true)], nil)) + }) +} + +func newGopherTilesHandler() http.Handler { + const gopherURL = "https://blog.golang.org/go-programming-language-turns-two_gophers.jpg" + res, err := http.Get(gopherURL) + if err != nil { + log.Fatal(err) + } + if res.StatusCode != 200 { + log.Fatalf("Error fetching %s: %v", gopherURL, res.Status) + } + slurp, err := ioutil.ReadAll(res.Body) + res.Body.Close() + if err != nil { + log.Fatal(err) + } + im, err := jpeg.Decode(bytes.NewReader(slurp)) + if err != nil { + if len(slurp) > 1024 { + slurp = slurp[:1024] + } + log.Fatalf("Failed to decode gopher image: %v (got %q)", err, slurp) + } + + type subImager interface { + SubImage(image.Rectangle) image.Image + } + const tileSize = 32 + xt := im.Bounds().Max.X / tileSize + yt := im.Bounds().Max.Y / tileSize + var tile [][][]byte // y -> x -> jpeg bytes + for yi := 0; yi < yt; yi++ { + var row [][]byte + for xi := 0; xi < xt; xi++ { + si := im.(subImager).SubImage(image.Rectangle{ + Min: image.Point{xi * tileSize, yi * tileSize}, + Max: image.Point{(xi + 1) * tileSize, (yi + 1) * tileSize}, + }) + buf := new(bytes.Buffer) + if err := jpeg.Encode(buf, si, &jpeg.Options{Quality: 90}); err != nil { + log.Fatal(err) + } + row = append(row, buf.Bytes()) + } + tile = append(tile, row) + } + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ms, _ := strconv.Atoi(r.FormValue("latency")) + const nanosPerMilli = 1e6 + if r.FormValue("x") != "" { + x, _ := strconv.Atoi(r.FormValue("x")) + y, _ := strconv.Atoi(r.FormValue("y")) + if ms <= 1000 { + time.Sleep(time.Duration(ms) * nanosPerMilli) + } + if x >= 0 && x < xt && y >= 0 && y < yt { + http.ServeContent(w, r, "", time.Time{}, bytes.NewReader(tile[y][x])) + return + } + } + io.WriteString(w, "") + fmt.Fprintf(w, "A grid of %d tiled images is below. Compare:

", xt*yt) + for _, ms := range []int{0, 30, 200, 1000} { + d := time.Duration(ms) * nanosPerMilli + fmt.Fprintf(w, "[HTTP/2, %v latency] [HTTP/1, %v latency]
\n", + httpsHost(), ms, d, + httpHost(), ms, d, + ) + } + io.WriteString(w, "

\n") + cacheBust := time.Now().UnixNano() + for y := 0; y < yt; y++ { + for x := 0; x < xt; x++ { + fmt.Fprintf(w, "", + tileSize, tileSize, x, y, cacheBust, ms) + } + io.WriteString(w, "
\n") + } + io.WriteString(w, `

+ +
<< Back to Go HTTP/2 demo server`) + }) +} + +func httpsHost() string { + if *hostHTTPS != "" { + return *hostHTTPS + } + if v := *httpsAddr; strings.HasPrefix(v, ":") { + return "localhost" + v + } else { + return v + } +} + +func httpHost() string { + if *hostHTTP != "" { + return *hostHTTP + } + if v := *httpAddr; strings.HasPrefix(v, ":") { + return "localhost" + v + } else { + return v + } +} + +func serveProdTLS() error { + const cacheDir = "/var/cache/autocert" + if err := os.MkdirAll(cacheDir, 0700); err != nil { + return err + } + m := autocert.Manager{ + Cache: autocert.DirCache(cacheDir), + Prompt: autocert.AcceptTOS, + HostPolicy: autocert.HostWhitelist("http2.golang.org"), + } + srv := &http.Server{ + TLSConfig: &tls.Config{ + GetCertificate: m.GetCertificate, + }, + } + http2.ConfigureServer(srv, &http2.Server{}) + ln, err := net.Listen("tcp", ":443") + if err != nil { + return err + } + return srv.Serve(tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig)) +} + +type tcpKeepAliveListener struct { + *net.TCPListener +} + +func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { + tc, err := ln.AcceptTCP() + if err != nil { + return + } + tc.SetKeepAlive(true) + tc.SetKeepAlivePeriod(3 * time.Minute) + return tc, nil +} + +func serveProd() error { + errc := make(chan error, 2) + go func() { errc <- http.ListenAndServe(":80", nil) }() + go func() { errc <- serveProdTLS() }() + return <-errc +} + +const idleTimeout = 5 * time.Minute +const activeTimeout = 10 * time.Minute + +// TODO: put this into the standard library and actually send +// PING frames and GOAWAY, etc: golang.org/issue/14204 +func idleTimeoutHook() func(net.Conn, http.ConnState) { + var mu sync.Mutex + m := map[net.Conn]*time.Timer{} + return func(c net.Conn, cs http.ConnState) { + mu.Lock() + defer mu.Unlock() + if t, ok := m[c]; ok { + delete(m, c) + t.Stop() + } + var d time.Duration + switch cs { + case http.StateNew, http.StateIdle: + d = idleTimeout + case http.StateActive: + d = activeTimeout + default: + return + } + m[c] = time.AfterFunc(d, func() { + log.Printf("closing idle conn %v after %v", c.RemoteAddr(), d) + go c.Close() + }) + } +} + +func main() { + var srv http.Server + flag.BoolVar(&http2.VerboseLogs, "verbose", false, "Verbose HTTP/2 debugging.") + flag.Parse() + srv.Addr = *httpsAddr + srv.ConnState = idleTimeoutHook() + + registerHandlers() + + if *prod { + *hostHTTP = "http2.golang.org" + *hostHTTPS = "http2.golang.org" + log.Fatal(serveProd()) + } + + url := "https://" + httpsHost() + "/" + log.Printf("Listening on " + url) + http2.ConfigureServer(&srv, &http2.Server{}) + + if *httpAddr != "" { + go func() { + log.Printf("Listening on http://" + httpHost() + "/ (for unencrypted HTTP/1)") + log.Fatal(http.ListenAndServe(*httpAddr, nil)) + }() + } + + go func() { + log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key")) + }() + select {} +} diff --git a/vendor/golang.org/x/net/http2/h2demo/launch.go b/vendor/golang.org/x/net/http2/h2demo/launch.go new file mode 100644 index 00000000..df0866a3 --- /dev/null +++ b/vendor/golang.org/x/net/http2/h2demo/launch.go @@ -0,0 +1,302 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "bufio" + "bytes" + "encoding/json" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "strings" + "time" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + compute "google.golang.org/api/compute/v1" +) + +var ( + proj = flag.String("project", "symbolic-datum-552", "name of Project") + zone = flag.String("zone", "us-central1-a", "GCE zone") + mach = flag.String("machinetype", "n1-standard-1", "Machine type") + instName = flag.String("instance_name", "http2-demo", "Name of VM instance.") + sshPub = flag.String("ssh_public_key", "", "ssh public key file to authorize. Can modify later in Google's web UI anyway.") + staticIP = flag.String("static_ip", "130.211.116.44", "Static IP to use. If empty, automatic.") + + writeObject = flag.String("write_object", "", "If non-empty, a VM isn't created and the flag value is Google Cloud Storage bucket/object to write. The contents from stdin.") + publicObject = flag.Bool("write_object_is_public", false, "Whether the object created by --write_object should be public.") +) + +func readFile(v string) string { + slurp, err := ioutil.ReadFile(v) + if err != nil { + log.Fatalf("Error reading %s: %v", v, err) + } + return strings.TrimSpace(string(slurp)) +} + +var config = &oauth2.Config{ + // The client-id and secret should be for an "Installed Application" when using + // the CLI. Later we'll use a web application with a callback. + ClientID: readFile("client-id.dat"), + ClientSecret: readFile("client-secret.dat"), + Endpoint: google.Endpoint, + Scopes: []string{ + compute.DevstorageFullControlScope, + compute.ComputeScope, + "https://www.googleapis.com/auth/sqlservice", + "https://www.googleapis.com/auth/sqlservice.admin", + }, + RedirectURL: "urn:ietf:wg:oauth:2.0:oob", +} + +const baseConfig = `#cloud-config +coreos: + units: + - name: h2demo.service + command: start + content: | + [Unit] + Description=HTTP2 Demo + + [Service] + ExecStartPre=/bin/bash -c 'mkdir -p /opt/bin && curl -s -o /opt/bin/h2demo http://storage.googleapis.com/http2-demo-server-tls/h2demo && chmod +x /opt/bin/h2demo' + ExecStart=/opt/bin/h2demo --prod + RestartSec=5s + Restart=always + Type=simple + + [Install] + WantedBy=multi-user.target +` + +func main() { + flag.Parse() + if *proj == "" { + log.Fatalf("Missing --project flag") + } + prefix := "https://www.googleapis.com/compute/v1/projects/" + *proj + machType := prefix + "/zones/" + *zone + "/machineTypes/" + *mach + + const tokenFileName = "token.dat" + tokenFile := tokenCacheFile(tokenFileName) + tokenSource := oauth2.ReuseTokenSource(nil, tokenFile) + token, err := tokenSource.Token() + if err != nil { + if *writeObject != "" { + log.Fatalf("Can't use --write_object without a valid token.dat file already cached.") + } + log.Printf("Error getting token from %s: %v", tokenFileName, err) + log.Printf("Get auth code from %v", config.AuthCodeURL("my-state")) + fmt.Print("\nEnter auth code: ") + sc := bufio.NewScanner(os.Stdin) + sc.Scan() + authCode := strings.TrimSpace(sc.Text()) + token, err = config.Exchange(oauth2.NoContext, authCode) + if err != nil { + log.Fatalf("Error exchanging auth code for a token: %v", err) + } + if err := tokenFile.WriteToken(token); err != nil { + log.Fatalf("Error writing to %s: %v", tokenFileName, err) + } + tokenSource = oauth2.ReuseTokenSource(token, nil) + } + + oauthClient := oauth2.NewClient(oauth2.NoContext, tokenSource) + + if *writeObject != "" { + writeCloudStorageObject(oauthClient) + return + } + + computeService, _ := compute.New(oauthClient) + + natIP := *staticIP + if natIP == "" { + // Try to find it by name. + aggAddrList, err := computeService.Addresses.AggregatedList(*proj).Do() + if err != nil { + log.Fatal(err) + } + // http://godoc.org/code.google.com/p/google-api-go-client/compute/v1#AddressAggregatedList + IPLoop: + for _, asl := range aggAddrList.Items { + for _, addr := range asl.Addresses { + if addr.Name == *instName+"-ip" && addr.Status == "RESERVED" { + natIP = addr.Address + break IPLoop + } + } + } + } + + cloudConfig := baseConfig + if *sshPub != "" { + key := strings.TrimSpace(readFile(*sshPub)) + cloudConfig += fmt.Sprintf("\nssh_authorized_keys:\n - %s\n", key) + } + if os.Getenv("USER") == "bradfitz" { + cloudConfig += fmt.Sprintf("\nssh_authorized_keys:\n - %s\n", "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAwks9dwWKlRC+73gRbvYtVg0vdCwDSuIlyt4z6xa/YU/jTDynM4R4W10hm2tPjy8iR1k8XhDv4/qdxe6m07NjG/By1tkmGpm1mGwho4Pr5kbAAy/Qg+NLCSdAYnnE00FQEcFOC15GFVMOW2AzDGKisReohwH9eIzHPzdYQNPRWXE= bradfitz@papag.bradfitz.com") + } + const maxCloudConfig = 32 << 10 // per compute API docs + if len(cloudConfig) > maxCloudConfig { + log.Fatalf("cloud config length of %d bytes is over %d byte limit", len(cloudConfig), maxCloudConfig) + } + + instance := &compute.Instance{ + Name: *instName, + Description: "Go Builder", + MachineType: machType, + Disks: []*compute.AttachedDisk{instanceDisk(computeService)}, + Tags: &compute.Tags{ + Items: []string{"http-server", "https-server"}, + }, + Metadata: &compute.Metadata{ + Items: []*compute.MetadataItems{ + { + Key: "user-data", + Value: &cloudConfig, + }, + }, + }, + NetworkInterfaces: []*compute.NetworkInterface{ + { + AccessConfigs: []*compute.AccessConfig{ + { + Type: "ONE_TO_ONE_NAT", + Name: "External NAT", + NatIP: natIP, + }, + }, + Network: prefix + "/global/networks/default", + }, + }, + ServiceAccounts: []*compute.ServiceAccount{ + { + Email: "default", + Scopes: []string{ + compute.DevstorageFullControlScope, + compute.ComputeScope, + }, + }, + }, + } + + log.Printf("Creating instance...") + op, err := computeService.Instances.Insert(*proj, *zone, instance).Do() + if err != nil { + log.Fatalf("Failed to create instance: %v", err) + } + opName := op.Name + log.Printf("Created. Waiting on operation %v", opName) +OpLoop: + for { + time.Sleep(2 * time.Second) + op, err := computeService.ZoneOperations.Get(*proj, *zone, opName).Do() + if err != nil { + log.Fatalf("Failed to get op %s: %v", opName, err) + } + switch op.Status { + case "PENDING", "RUNNING": + log.Printf("Waiting on operation %v", opName) + continue + case "DONE": + if op.Error != nil { + for _, operr := range op.Error.Errors { + log.Printf("Error: %+v", operr) + } + log.Fatalf("Failed to start.") + } + log.Printf("Success. %+v", op) + break OpLoop + default: + log.Fatalf("Unknown status %q: %+v", op.Status, op) + } + } + + inst, err := computeService.Instances.Get(*proj, *zone, *instName).Do() + if err != nil { + log.Fatalf("Error getting instance after creation: %v", err) + } + ij, _ := json.MarshalIndent(inst, "", " ") + log.Printf("Instance: %s", ij) +} + +func instanceDisk(svc *compute.Service) *compute.AttachedDisk { + const imageURL = "https://www.googleapis.com/compute/v1/projects/coreos-cloud/global/images/coreos-stable-444-5-0-v20141016" + diskName := *instName + "-disk" + + return &compute.AttachedDisk{ + AutoDelete: true, + Boot: true, + Type: "PERSISTENT", + InitializeParams: &compute.AttachedDiskInitializeParams{ + DiskName: diskName, + SourceImage: imageURL, + DiskSizeGb: 50, + }, + } +} + +func writeCloudStorageObject(httpClient *http.Client) { + content := os.Stdin + const maxSlurp = 1 << 20 + var buf bytes.Buffer + n, err := io.CopyN(&buf, content, maxSlurp) + if err != nil && err != io.EOF { + log.Fatalf("Error reading from stdin: %v, %v", n, err) + } + contentType := http.DetectContentType(buf.Bytes()) + + req, err := http.NewRequest("PUT", "https://storage.googleapis.com/"+*writeObject, io.MultiReader(&buf, content)) + if err != nil { + log.Fatal(err) + } + req.Header.Set("x-goog-api-version", "2") + if *publicObject { + req.Header.Set("x-goog-acl", "public-read") + } + req.Header.Set("Content-Type", contentType) + res, err := httpClient.Do(req) + if err != nil { + log.Fatal(err) + } + if res.StatusCode != 200 { + res.Write(os.Stderr) + log.Fatalf("Failed.") + } + log.Printf("Success.") + os.Exit(0) +} + +type tokenCacheFile string + +func (f tokenCacheFile) Token() (*oauth2.Token, error) { + slurp, err := ioutil.ReadFile(string(f)) + if err != nil { + return nil, err + } + t := new(oauth2.Token) + if err := json.Unmarshal(slurp, t); err != nil { + return nil, err + } + return t, nil +} + +func (f tokenCacheFile) WriteToken(t *oauth2.Token) error { + jt, err := json.Marshal(t) + if err != nil { + return err + } + return ioutil.WriteFile(string(f), jt, 0600) +} diff --git a/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go b/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go index 1b649560..26bc838d 100644 --- a/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go +++ b/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go @@ -23,7 +23,7 @@ import ( "golang.org/x/oauth2/internal" ) -// Client Credentials Config describes a 2-legged OAuth2 flow, with both the +// Config describes a 2-legged OAuth2 flow, with both the // client application information and the server's endpoint URLs. type Config struct { // ClientID is the application's ID. diff --git a/vendor/golang.org/x/oauth2/google/sdk.go b/vendor/golang.org/x/oauth2/google/sdk.go index d29a3bb9..bdc18084 100644 --- a/vendor/golang.org/x/oauth2/google/sdk.go +++ b/vendor/golang.org/x/oauth2/google/sdk.go @@ -160,9 +160,13 @@ var sdkConfigPath = func() (string, error) { } func guessUnixHomeDir() string { - usr, err := user.Current() - if err == nil { - return usr.HomeDir + // Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470 + if v := os.Getenv("HOME"); v != "" { + return v } - return os.Getenv("HOME") + // Else, fall back to user.Current: + if u, err := user.Current(); err == nil { + return u.HomeDir + } + return "" } diff --git a/vendor/golang.org/x/oauth2/internal/oauth2.go b/vendor/golang.org/x/oauth2/internal/oauth2.go index fbe1028d..e31541b3 100644 --- a/vendor/golang.org/x/oauth2/internal/oauth2.go +++ b/vendor/golang.org/x/oauth2/internal/oauth2.go @@ -42,7 +42,7 @@ func ParseKey(key []byte) (*rsa.PrivateKey, error) { func ParseINI(ini io.Reader) (map[string]map[string]string, error) { result := map[string]map[string]string{ - "": map[string]string{}, // root section + "": {}, // root section } scanner := bufio.NewScanner(ini) currentSection := "" diff --git a/vendor/golang.org/x/oauth2/internal/token.go b/vendor/golang.org/x/oauth2/internal/token.go index 18328a0d..ba90a341 100644 --- a/vendor/golang.org/x/oauth2/internal/token.go +++ b/vendor/golang.org/x/oauth2/internal/token.go @@ -91,6 +91,7 @@ func (e *expirationTime) UnmarshalJSON(b []byte) error { var brokenAuthHeaderProviders = []string{ "https://accounts.google.com/", + "https://api.codeswholesale.com/oauth/token", "https://api.dropbox.com/", "https://api.dropboxapi.com/", "https://api.instagram.com/", @@ -101,6 +102,7 @@ var brokenAuthHeaderProviders = []string{ "https://api.twitch.tv/", "https://app.box.com/", "https://connect.stripe.com/", + "https://graph.facebook.com", // see https://github.com/golang/oauth2/issues/214 "https://login.microsoftonline.com/", "https://login.salesforce.com/", "https://oauth.sandbox.trainingpeaks.com/", @@ -117,6 +119,7 @@ var brokenAuthHeaderProviders = []string{ "https://www.strava.com/oauth/", "https://www.wunderlist.com/oauth/", "https://api.patreon.com/", + "https://sandbox.codeswholesale.com/oauth/token", } func RegisterBrokenAuthHeaderProvider(tokenURL string) { @@ -151,9 +154,9 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, if err != nil { return nil, err } - v.Set("client_id", clientID) bustedAuth := !providerAuthHeaderWorks(tokenURL) if bustedAuth && clientSecret != "" { + v.Set("client_id", clientID) v.Set("client_secret", clientSecret) } req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode())) diff --git a/vendor/golang.org/x/oauth2/jwt/jwt.go b/vendor/golang.org/x/oauth2/jwt/jwt.go index f4b9523e..e016db42 100644 --- a/vendor/golang.org/x/oauth2/jwt/jwt.go +++ b/vendor/golang.org/x/oauth2/jwt/jwt.go @@ -105,7 +105,9 @@ func (js jwtSource) Token() (*oauth2.Token, error) { if t := js.conf.Expires; t > 0 { claimSet.Exp = time.Now().Add(t).Unix() } - payload, err := jws.Encode(defaultHeader, claimSet, pk) + h := *defaultHeader + h.KeyID = js.conf.PrivateKeyID + payload, err := jws.Encode(&h, claimSet, pk) if err != nil { return nil, err } diff --git a/vendor/golang.org/x/oauth2/oauth2.go b/vendor/golang.org/x/oauth2/oauth2.go index 7b06bfe1..3e4835d7 100644 --- a/vendor/golang.org/x/oauth2/oauth2.go +++ b/vendor/golang.org/x/oauth2/oauth2.go @@ -180,7 +180,6 @@ func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) { "grant_type": {"authorization_code"}, "code": {code}, "redirect_uri": internal.CondVal(c.RedirectURL), - "scope": internal.CondVal(strings.Join(c.Scopes, " ")), }) } diff --git a/vendor/gopkg.in/yaml.v2/decode.go b/vendor/gopkg.in/yaml.v2/decode.go index a033516c..085cddc4 100644 --- a/vendor/gopkg.in/yaml.v2/decode.go +++ b/vendor/gopkg.in/yaml.v2/decode.go @@ -120,6 +120,7 @@ func (p *parser) parse() *node { default: panic("attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ))) } + panic("unreachable") } func (p *parser) node(kind int) *node { diff --git a/vendor/gopkg.in/yaml.v2/emitterc.go b/vendor/gopkg.in/yaml.v2/emitterc.go index 6ecdcb3c..2befd553 100644 --- a/vendor/gopkg.in/yaml.v2/emitterc.go +++ b/vendor/gopkg.in/yaml.v2/emitterc.go @@ -666,6 +666,7 @@ func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, return yaml_emitter_set_emitter_error(emitter, "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS") } + return false } // Expect ALIAS. diff --git a/vendor/gopkg.in/yaml.v2/parserc.go b/vendor/gopkg.in/yaml.v2/parserc.go index 81d05dfe..0a7037ad 100644 --- a/vendor/gopkg.in/yaml.v2/parserc.go +++ b/vendor/gopkg.in/yaml.v2/parserc.go @@ -166,6 +166,7 @@ func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool default: panic("invalid parser state") } + return false } // Parse the production: diff --git a/vendor/manifest b/vendor/manifest index f1f10f40..ccd15228 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -224,7 +224,7 @@ "importpath": "github.com/vmware/govmomi", "repository": "https://github.com/vmware/govmomi", "vcs": "git", - "revision": "c8df70bf7e64cb14c5305f4e16597c501a2eba8b", + "revision": "17b8c9ccb7f8c7b015d44c4ea39305c970a7bf31", "branch": "HEAD", "notests": true }, @@ -268,7 +268,7 @@ "importpath": "golang.org/x/crypto/ssh", "repository": "https://go.googlesource.com/crypto", "vcs": "git", - "revision": "8a549a1948fc5271eb24f36dcb0d3b47dec75a16", + "revision": "9f005a07e0d31d45e6656d241bb5c0f2efd4bc94", "branch": "master", "path": "/ssh", "notests": true @@ -322,7 +322,7 @@ "importpath": "golang.org/x/oauth2", "repository": "https://go.googlesource.com/oauth2", "vcs": "git", - "revision": "f6093e37b6cb4092101a298aba5d794eb570757f", + "revision": "b9780ec78894ab900c062d58ee3076cd9b2a4501", "branch": "master", "notests": true }, diff --git a/virtualmachine/aws/util.go b/virtualmachine/aws/util.go index 833966db..6a9fd938 100644 --- a/virtualmachine/aws/util.go +++ b/virtualmachine/aws/util.go @@ -25,7 +25,6 @@ const ( defaultInstanceType = "t2.micro" defaultAMI = "ami-5189a661" // ubuntu free tier defaultVolumeSize = 8 // GB - defaultDeviceName = "/dev/sda1" defaultVolumeType = "gp2" // RegionEnv is the env var for the AWS region. diff --git a/virtualmachine/aws/vm.go b/virtualmachine/aws/vm.go index 3173b328..f7c4991b 100644 --- a/virtualmachine/aws/vm.go +++ b/virtualmachine/aws/vm.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "net" + "sync" "time" "github.com/apcera/libretto/ssh" @@ -32,17 +33,19 @@ const ( StatePending = "pending" ) -// SSHTimeout is the maximum time to wait before failing to GetSSH. This is not -// thread-safe. -var SSHTimeout = 5 * time.Minute - var ( + // SSHTimeout is the maximum time to wait before failing to GetSSH. This is + // not thread-safe. + SSHTimeout = 5 * time.Minute + // This ensures that aws.VM implements the virtualmachine.VirtualMachine // interface at compile time. _ virtualmachine.VirtualMachine = (*VM)(nil) - // limiter rate limits channel to prevent saturating AWS API limits. - limiter = time.Tick(time.Millisecond * 500) + // nextProvision is the wall time when the next call to Provision will be + // allowed to proceed. This is part of the rate limiting system. + nextProvision time.Time + mu sync.Mutex ) var ( @@ -254,7 +257,8 @@ func (vm *VM) SetTags(tags map[string]string) error { // there was a problem during creation, if there was a problem adding a tag, or // if the VM takes too long to enter "running" state. func (vm *VM) Provision() error { - <-limiter + wait() // Avoid the AWS rate limit. + svc, err := getService(vm.Region) if err != nil { return fmt.Errorf("failed to get AWS service: %v", err) @@ -288,6 +292,50 @@ func (vm *VM) Provision() error { return nil } +// wait implements a rate limiter that prevents more than one call every +// 0.5s. The maximum time that the caller can be delayed is 1m. +func wait() { + const maxWait = 1 * time.Minute + + now := time.Now().UTC() + mu.Lock() + wait := getWaitTime(now, maxWait) + mu.Unlock() + + time.Sleep(wait) + + interval := 500 * time.Millisecond + if wait == maxWait { + interval = maxWait + } + + mu.Lock() + defer mu.Unlock() + if now.Before(nextProvision) { + nextProvision = nextProvision.Add(interval) + return + } + now = time.Now().UTC() + nextProvision = now.Add(interval) +} + +// getWaitTime computes the duration to sleep before the caller of wait() is +// allowed to proceed. Every concurrent call adds 0.5s to the time the +// subsequent caller must wait, up to a maximum of 1 minute. +func getWaitTime(now time.Time, maxWait time.Duration) time.Duration { + wait := nextProvision.Sub(now) // might be negative + + // When the system clock falls back an hour, the wait time might be an hour. + // This sanity check prevents this error. + if wait > maxWait { + return maxWait + } + if wait < 0 { + return 0 + } + return wait +} + // GetIPs returns a slice of IP addresses assigned to the VM. The PublicIP or // PrivateIP consts can be used to retrieve respective IP address type. It // returns nil if there was an error obtaining the IPs. diff --git a/virtualmachine/azure/arm/templates.go b/virtualmachine/azure/arm/templates.go index 1a333aa1..a8cf839d 100644 --- a/virtualmachine/azure/arm/templates.go +++ b/virtualmachine/azure/arm/templates.go @@ -25,7 +25,7 @@ const Linux = `{ }, "nic": { "type": "string" - }, + }, "os_file": { "type": "string" }, @@ -144,7 +144,7 @@ const Linux = `{ "computerName": "[parameters('vm_name')]", "adminUsername": "[parameters('username')]", "linuxConfiguration": { - "disablePasswordAuthentication": "true", + "disablePasswordAuthentication": true, "ssh": { "publicKeys": [ { @@ -181,7 +181,7 @@ const Linux = `{ }, "diagnosticsProfile": { "bootDiagnostics": { - "enabled": "false" + "enabled": false } } } diff --git a/virtualmachine/azure/arm/util.go b/virtualmachine/azure/arm/util.go index 5599042d..8c25ca4b 100644 --- a/virtualmachine/azure/arm/util.go +++ b/virtualmachine/azure/arm/util.go @@ -16,17 +16,19 @@ import ( "github.com/Azure/azure-sdk-for-go/arm/network" "github.com/Azure/azure-sdk-for-go/arm/resources/resources" "github.com/Azure/azure-sdk-for-go/storage" + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/adal" "github.com/Azure/go-autorest/autorest/azure" ) // getServicePrincipalToken retrieves a new ServicePrincipalToken using values of the // passed credentials map. -func getServicePrincipalToken(creds *OAuthCredentials, scope string) (*azure.ServicePrincipalToken, error) { - oauthConfig, err := azure.PublicCloud.OAuthConfigForTenant(creds.TenantID) +func getServicePrincipalToken(creds *OAuthCredentials, scope string) (*adal.ServicePrincipalToken, error) { + oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, creds.TenantID) if err != nil { return nil, err } - return azure.NewServicePrincipalToken(*oauthConfig, creds.ClientID, creds.ClientSecret, scope) + return adal.NewServicePrincipalToken(*oauthConfig, creds.ClientID, creds.ClientSecret, scope) } type armParameter struct { @@ -146,8 +148,8 @@ func validateVM(vm *VM) error { // deploy deploys the given VM based on the default Linux arm template over the // VM's resource group. func (vm *VM) deploy() error { - // Set up the authorizer - authorizer, err := getServicePrincipalToken(&vm.Creds, azure.PublicCloud.ResourceManagerEndpoint) + // Get the auth token. + tok, err := getServicePrincipalToken(&vm.Creds, azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return err } @@ -161,10 +163,10 @@ func (vm *VM) deploy() error { // Create and send the deployment to the resource group deploymentsClient := resources.NewDeploymentsClient(vm.Creds.SubscriptionID) - deploymentsClient.Authorizer = authorizer + deploymentsClient.Authorizer = autorest.NewBearerAuthorizer(tok) - _, err = deploymentsClient.CreateOrUpdate(vm.ResourceGroup, vm.DeploymentName, *deployment, nil) - if err != nil { + _, errc := deploymentsClient.CreateOrUpdate(vm.ResourceGroup, vm.DeploymentName, *deployment, nil) + if err := <-errc; err != nil { return err } @@ -187,7 +189,7 @@ func (vm *VM) deploy() error { } // getPublicIP returns the public IP of the given VM, if exists one. -func (vm *VM) getPublicIP(authorizer *azure.ServicePrincipalToken) (net.IP, error) { +func (vm *VM) getPublicIP(authorizer autorest.Authorizer) (net.IP, error) { publicIPAddressesClient := network.NewPublicIPAddressesClient(vm.Creds.SubscriptionID) publicIPAddressesClient.Authorizer = authorizer @@ -196,92 +198,109 @@ func (vm *VM) getPublicIP(authorizer *azure.ServicePrincipalToken) (net.IP, erro return nil, err } - if resPublicIP.Properties == nil || resPublicIP.Properties.IPAddress == nil || - *resPublicIP.Properties.IPAddress == "" { + if resPublicIP.PublicIPAddressPropertiesFormat == nil || resPublicIP.PublicIPAddressPropertiesFormat.IPAddress == nil || + *resPublicIP.PublicIPAddressPropertiesFormat.IPAddress == "" { return nil, fmt.Errorf("VM has no public IP address") } - return net.ParseIP(*resPublicIP.Properties.IPAddress), nil + return net.ParseIP(*resPublicIP.PublicIPAddressPropertiesFormat.IPAddress), nil } // getPrivateIP returns the private IP of the given VM, if exists one. -func (vm *VM) getPrivateIP(authorizer *azure.ServicePrincipalToken) (net.IP, error) { +func (vm *VM) getPrivateIP(authorizer autorest.Authorizer) (net.IP, error) { interfaceClient := network.NewInterfacesClient(vm.Creds.SubscriptionID) interfaceClient.Authorizer = authorizer - resPrivateIP, err := interfaceClient.Get(vm.ResourceGroup, vm.Nic, "") + iface, err := interfaceClient.Get(vm.ResourceGroup, vm.Nic, "") if err != nil { return nil, err } - if resPrivateIP.Properties == nil || resPrivateIP.Properties.IPConfigurations == nil || - len(*resPrivateIP.Properties.IPConfigurations) == 0 { + if iface.InterfacePropertiesFormat == nil || iface.InterfacePropertiesFormat.IPConfigurations == nil || + len(*iface.InterfacePropertiesFormat.IPConfigurations) == 0 { return nil, fmt.Errorf("VM has no private IP address") } - ipConfigs := *resPrivateIP.Properties.IPConfigurations + ipConfigs := *iface.InterfacePropertiesFormat.IPConfigurations if len(ipConfigs) > 1 { return nil, fmt.Errorf("VM has multiple private IP addresses") } - return net.ParseIP(*ipConfigs[0].Properties.PrivateIPAddress), nil + return net.ParseIP(*ipConfigs[0].InterfaceIPConfigurationPropertiesFormat.PrivateIPAddress), nil } // deleteOSFile deletes the OS file from the VM's storage account, returns an error if the operation // does not succeed. -func (vm *VM) deleteVMFiles(authorizer *azure.ServicePrincipalToken) error { +func (vm *VM) deleteVMFiles(authorizer autorest.Authorizer) error { storageAccountsClient := armStorage.NewAccountsClient(vm.Creds.SubscriptionID) storageAccountsClient.Authorizer = authorizer - accountKeys, err := storageAccountsClient.ListKeys(vm.ResourceGroup, vm.StorageAccount) + result, err := storageAccountsClient.ListKeys(vm.ResourceGroup, vm.StorageAccount) if err != nil { return err } - storageClient, err := storage.NewBasicClient(vm.StorageAccount, *accountKeys.Key1) + accountKeys := result.Keys + if accountKeys == nil || len(*accountKeys) == 0 { + return fmt.Errorf("no account keys for storage account %q", vm.StorageAccount) + } + + accountKey := *(*accountKeys)[0].Value + storageClient, err := storage.NewBasicClient(vm.StorageAccount, accountKey) if err != nil { return err } + // Get a reference to the OS file. blobStorageClient := storageClient.GetBlobService() - err = blobStorageClient.DeleteBlob(vm.StorageContainer, vm.OsFile, nil) - if err != nil { + container := blobStorageClient.GetContainerReference(vm.StorageContainer) + osFileBlob := container.GetBlobReference(vm.OsFile) + + // Delete the OS file. + opts := &storage.DeleteBlobOptions{Timeout: 30} // 30s timeout + err = osFileBlob.Delete(opts) + + if vm.DiskSize <= 0 { return err } - if vm.DiskSize <= 0 { - return nil + + // Delete the disk file. + diskFileBlob := container.GetBlobReference(vm.DiskFile) + diskFileErr := diskFileBlob.Delete(opts) + if err != nil { + return fmt.Errorf("failed to delete OS file and disk file: %v, %v", err, diskFileErr) } - return blobStorageClient.DeleteBlob(vm.StorageContainer, vm.DiskFile, nil) + return diskFileErr } // deleteNic deletes the network interface for the given VM from the VM's resource group, returns an error // if the operation does not succeed. -func (vm *VM) deleteNic(authorizer *azure.ServicePrincipalToken) error { +func (vm *VM) deleteNic(authorizer autorest.Authorizer) error { interfaceClient := network.NewInterfacesClient(vm.Creds.SubscriptionID) interfaceClient.Authorizer = authorizer - _, err := interfaceClient.Delete(vm.ResourceGroup, vm.Nic, nil) - return err + _, errc := interfaceClient.Delete(vm.ResourceGroup, vm.Nic, nil) + return <-errc } // deletePublicIP deletes the reserved Public IP of the given VM from the VM's resource group, returns an error // if the operation does not succeed. -func (vm *VM) deletePublicIP(authorizer *azure.ServicePrincipalToken) error { +func (vm *VM) deletePublicIP(authorizer autorest.Authorizer) error { // Delete the Public IP of this VM publicIPAddressesClient := network.NewPublicIPAddressesClient(vm.Creds.SubscriptionID) publicIPAddressesClient.Authorizer = authorizer - _, err := publicIPAddressesClient.Delete(vm.ResourceGroup, vm.PublicIP, nil) - return err + _, errc := publicIPAddressesClient.Delete(vm.ResourceGroup, vm.PublicIP, nil) + return <-errc } // deleteDeployment deletes the deployed azure arm template for this vm. -func (vm *VM) deleteDeployment(authorizer *azure.ServicePrincipalToken) error { +func (vm *VM) deleteDeployment(authorizer autorest.Authorizer) error { // Get the deployments client deploymentsClient := resources.NewDeploymentsClient(vm.Creds.SubscriptionID) deploymentsClient.Authorizer = authorizer // Delete the deployment - _, err := deploymentsClient.Delete(vm.ResourceGroup, vm.DeploymentName, nil) - return err + _, errc := deploymentsClient.Delete(vm.ResourceGroup, vm.DeploymentName, nil) + return <-errc } func createDeployment(template string, params armParameters) (*resources.Deployment, error) { diff --git a/virtualmachine/azure/arm/vm.go b/virtualmachine/azure/arm/vm.go index 2eced98b..5be8680a 100644 --- a/virtualmachine/azure/arm/vm.go +++ b/virtualmachine/azure/arm/vm.go @@ -15,6 +15,7 @@ import ( lvm "github.com/apcera/libretto/virtualmachine" "github.com/Azure/azure-sdk-for-go/arm/compute" + "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/azure" ) @@ -114,7 +115,7 @@ func (vm *VM) Provision() error { } // Set up private members of the VM - tempName := fmt.Sprintf("%s", randStringRunes(5)) + tempName := randStringRunes(5) if vm.OsFile == "" { vm.OsFile = tempName + "-os-disk.vhd" } @@ -131,10 +132,11 @@ func (vm *VM) Provision() error { vm.DeploymentName = tempName + "-deploy" } - // Create and send the deployment - vm.deploy() + err = vm.deploy() + if err != nil { + return err + } - // Use GetSSH to try to connect to machine cli, err := vm.GetSSH(ssh.Options{KeepAlive: 2}) if err != nil { return err @@ -147,11 +149,12 @@ func (vm *VM) Provision() error { func (vm *VM) GetIPs() ([]net.IP, error) { ips := make([]net.IP, 2) - // Set up the authorizer - authorizer, err := getServicePrincipalToken(&vm.Creds, azure.PublicCloud.ResourceManagerEndpoint) + // Set up the auth token. + tok, err := getServicePrincipalToken(&vm.Creds, azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return nil, err } + authorizer := autorest.NewBearerAuthorizer(tok) // Get the Public IP ip, err := vm.getPublicIP(authorizer) @@ -193,10 +196,11 @@ func (vm *VM) GetSSH(options ssh.Options) (ssh.Client, error) { // "stopped" func (vm *VM) GetState() (string, error) { // Set up the authorizer - authorizer, err := getServicePrincipalToken(&vm.Creds, azure.PublicCloud.ResourceManagerEndpoint) + tok, err := getServicePrincipalToken(&vm.Creds, azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return "", err } + authorizer := autorest.NewBearerAuthorizer(tok) virtualMachinesClient := compute.NewVirtualMachinesClient(vm.Creds.SubscriptionID) virtualMachinesClient.Authorizer = authorizer @@ -206,8 +210,8 @@ func (vm *VM) GetState() (string, error) { return "", e } - if r.Properties != nil && r.Properties.InstanceView != nil && len(*r.Properties.InstanceView.Statuses) > 1 { - state := *(*r.Properties.InstanceView.Statuses)[1].DisplayStatus + if r.VirtualMachineProperties != nil && r.VirtualMachineProperties.InstanceView != nil && len(*r.VirtualMachineProperties.InstanceView.Statuses) > 1 { + state := *(*r.VirtualMachineProperties.InstanceView.Statuses)[1].DisplayStatus return translateState(state), e } @@ -217,17 +221,18 @@ func (vm *VM) GetState() (string, error) { // Destroy deletes the VM on Azure. func (vm *VM) Destroy() error { // Set up the authorizer - authorizer, err := getServicePrincipalToken(&vm.Creds, azure.PublicCloud.ResourceManagerEndpoint) + tok, err := getServicePrincipalToken(&vm.Creds, azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return err } + authorizer := autorest.NewBearerAuthorizer(tok) // Delete the VM virtualMachinesClient := compute.NewVirtualMachinesClient(vm.Creds.SubscriptionID) virtualMachinesClient.Authorizer = authorizer - _, err = virtualMachinesClient.Delete(vm.ResourceGroup, vm.Name, nil) - if err != nil { + _, errc := virtualMachinesClient.Delete(vm.ResourceGroup, vm.Name, nil) + if err := <-errc; err != nil { return err } @@ -295,17 +300,18 @@ func (vm *VM) Destroy() error { // Halt shuts down the VM. func (vm *VM) Halt() error { // Set up the authorizer - authorizer, err := getServicePrincipalToken(&vm.Creds, azure.PublicCloud.ResourceManagerEndpoint) + tok, err := getServicePrincipalToken(&vm.Creds, azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return err } + authorizer := autorest.NewBearerAuthorizer(tok) // Poweroff the VM virtualMachinesClient := compute.NewVirtualMachinesClient(vm.Creds.SubscriptionID) virtualMachinesClient.Authorizer = authorizer - _, err = virtualMachinesClient.PowerOff(vm.ResourceGroup, vm.Name, nil) - if err != nil { + _, errc := virtualMachinesClient.PowerOff(vm.ResourceGroup, vm.Name, nil) + if err := <-errc; err != nil { return err } @@ -327,17 +333,18 @@ func (vm *VM) Halt() error { // Start boots a stopped VM. func (vm *VM) Start() error { // Set up the authorizer - authorizer, err := getServicePrincipalToken(&vm.Creds, azure.PublicCloud.ResourceManagerEndpoint) + tok, err := getServicePrincipalToken(&vm.Creds, azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return err } + authorizer := autorest.NewBearerAuthorizer(tok) // Start the VM virtualMachinesClient := compute.NewVirtualMachinesClient(vm.Creds.SubscriptionID) virtualMachinesClient.Authorizer = authorizer - _, err = virtualMachinesClient.Start(vm.ResourceGroup, vm.Name, nil) - if err != nil { + _, errc := virtualMachinesClient.Start(vm.ResourceGroup, vm.Name, nil) + if err := <-errc; err != nil { return err } diff --git a/virtualmachine/azure/management/util.go b/virtualmachine/azure/management/util.go index 52ed8481..c812ba9b 100644 --- a/virtualmachine/azure/management/util.go +++ b/virtualmachine/azure/management/util.go @@ -6,7 +6,6 @@ import ( "encoding/base64" "fmt" "sync" - "time" lvm "github.com/apcera/libretto/virtualmachine" @@ -146,23 +145,6 @@ func (vm *VM) serviceExist(services []hostedservice.HostedService) bool { return false } -// waitForReady waits for the VM to go into the desired state. -func (vm *VM) waitForReady(timeout int, targetState string) error { - for i := 0; i < timeout; i++ { - state, err := vm.GetState() - if err != nil { - return err - } - - if state == targetState { - return nil - } - - time.Sleep(1 * time.Second) - } - return fmt.Errorf(errMsgTimeout, virtualmachine.DeploymentStatusRunning) -} - func (vm *VM) getDeploymentOptions() virtualmachine.CreateDeploymentOptions { vnn := vm.DeployOptions.VirtualNetworkName if vnn == "" { diff --git a/virtualmachine/azure/management/vm.go b/virtualmachine/azure/management/vm.go index 72ee4e46..629670e8 100644 --- a/virtualmachine/azure/management/vm.go +++ b/virtualmachine/azure/management/vm.go @@ -24,9 +24,7 @@ const ( PrivateIP = 1 errGetClient = "Error to retrieve Azure client %s" - errGetDeployment = "Error to provision Azure VM %s" errGetListService = "Error to list hosted services %s" - errMsgTimeout = "Time out waiting for instance to %s" errProvisionVM = "Error to provision Azure VM %s" ) diff --git a/virtualmachine/exoscale/util.go b/virtualmachine/exoscale/util.go index 67bfa71d..537d3336 100644 --- a/virtualmachine/exoscale/util.go +++ b/virtualmachine/exoscale/util.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "github.com/pyr/egoscale/src/egoscale" + "github.com/pyr/egoscale" ) func (vm *VM) getExoClient() *egoscale.Client { diff --git a/virtualmachine/exoscale/vm.go b/virtualmachine/exoscale/vm.go index 11720b95..0c41e70e 100644 --- a/virtualmachine/exoscale/vm.go +++ b/virtualmachine/exoscale/vm.go @@ -11,7 +11,7 @@ import ( "github.com/apcera/libretto/ssh" "github.com/apcera/libretto/util" "github.com/apcera/libretto/virtualmachine" - "github.com/pyr/egoscale/src/egoscale" + "github.com/pyr/egoscale" ) // VM represents an Exoscale virtual machine. diff --git a/virtualmachine/gcp/util.go b/virtualmachine/gcp/util.go index e6f7b781..b1e1bcb3 100644 --- a/virtualmachine/gcp/util.go +++ b/virtualmachine/gcp/util.go @@ -3,6 +3,7 @@ package gcp import ( + "context" "encoding/json" "errors" "fmt" @@ -12,14 +13,13 @@ import ( "os" "strings" "time" - "context" "golang.org/x/oauth2" "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" - googlecloud "google.golang.org/api/compute/v1" googleresourcemanager "google.golang.org/api/cloudresourcemanager/v1" + googlecloud "google.golang.org/api/compute/v1" ) var ( @@ -102,7 +102,7 @@ func (vm *VM) getService() (*googleService, error) { Scopes: vm.Scopes, TokenURL: tokenURL, } - client = config.Client(oauth2.NoContext) + client = config.Client(context.Background()) } else { client = &http.Client{ Timeout: time.Duration(30 * time.Second), @@ -724,7 +724,7 @@ func (svc *googleService) removeFirewallRules() error { newRules[indexEndp].Ports[indPort] = "" newRules[indexEndp].Ports = append( newRules[indexEndp].Ports[:indPort], - newRules[indexEndp].Ports[indPort+1:]... + newRules[indexEndp].Ports[indPort+1:]..., ) // Set the index back by one because we have // shifted elements from indP onwards to left diff --git a/virtualmachine/openstack/util.go b/virtualmachine/openstack/util.go index 64bb0992..04f6ce6b 100644 --- a/virtualmachine/openstack/util.go +++ b/virtualmachine/openstack/util.go @@ -14,12 +14,12 @@ import ( "strings" "time" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" - "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes" - "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach" - "github.com/rackspace/gophercloud/openstack/compute/v2/images" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" + "github.com/gophercloud/gophercloud/openstack/compute/v2/images" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/apcera/libretto/ssh" lvm "github.com/apcera/libretto/virtualmachine" @@ -505,32 +505,11 @@ func deattachAndDeleteVolume(vm *VM) error { return nil } -// Delete the instance -func deleteVM(client *gophercloud.ServiceClient, vm *VM) error { - err := servers.Delete(client, vm.InstanceID).ExtractErr() +// deleteVM deletes the instance. +func deleteVM(client *gophercloud.ServiceClient, vmID string) error { + err := servers.Delete(client, vmID).ExtractErr() if err != nil { - return fmt.Errorf("failed to destroy the vm: %s", err) - } - - // Wait until its status becomes nil within ActionTimeout seconds. - var server *servers.Server - for i := 0; i < ActionTimeout; i++ { - server, err = getServer(vm) - if err != nil { - return err - } - - if server == nil { - break - } else if server.Status == StateError { - return fmt.Errorf("error on destroying the vm") - } - - time.Sleep(1 * time.Second) - } - - if server != nil { - return ErrActionTimeout + return fmt.Errorf("failed to destroy vm %s: %s", vmID, err) } return nil } diff --git a/virtualmachine/openstack/vm.go b/virtualmachine/openstack/vm.go index 59739285..10b3ff05 100644 --- a/virtualmachine/openstack/vm.go +++ b/virtualmachine/openstack/vm.go @@ -3,6 +3,7 @@ package openstack import ( + "encoding/json" "errors" "fmt" "net" @@ -11,12 +12,12 @@ import ( "github.com/apcera/libretto/ssh" "github.com/apcera/libretto/util" lvm "github.com/apcera/libretto/virtualmachine" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip" - ss "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop" - "github.com/rackspace/gophercloud/openstack/compute/v2/flavors" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" - "github.com/rackspace/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" + ss "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop" + "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) // Compiler will complain if openstack.VM doesn't implement VirtualMachine interface. @@ -142,7 +143,8 @@ type VM struct { Volume Volume // UUID of this instance (server). Set after provisioning - InstanceID string // optional + InstanceID string + // Instance Name of the VM (optional) Name string @@ -153,7 +155,7 @@ type VM struct { // to the VM. FloatingIPPool string // FloatingIP is the object that stores the necessary floating ip information for this VM - FloatingIP *floatingip.FloatingIP + FloatingIP *floatingips.FloatingIP // SecurityGroup represents the name of the security group to which this VM should belong SecurityGroup string @@ -174,6 +176,91 @@ type VM struct { computeClient *gophercloud.ServiceClient } +// MarshalJSON serializes the VM object to JSON. It includes the FloatingIP.ID +// field which would otherwise be omitted. We can't delete the floating IPs +// during a Destroy() call without knowing the floating IP ID. +func (vm *VM) MarshalJSON() ([]byte, error) { + // Make an alias of the VM type to avoid infinite recursion. This works + // because the alias does not have a MarshalJSON() method. + type ( + // credsAlias prevents a mutex in ssh.Credentials from being copied. + credsAlias struct { + SSHUser string + SSHPassword string + SSHPrivateKey string + } + vmAlias struct { + IdentityEndpoint string + Username string + Password string + Region string + TenantName string + FlavorName string + ImageID string + ImageMetadata ImageMetadata + ImagePath string + Volume Volume + InstanceID string + Name string + Networks []string + FloatingIPPool string + FloatingIP *floatingips.FloatingIP + SecurityGroup string + UserData []byte + AdminPassword string + Credentials credsAlias + } + ) + + // Creating the alias in this way avoids copying the mutex in + // ssh.Credentials, which go vet doesn't like. + alias := vmAlias{ + IdentityEndpoint: vm.IdentityEndpoint, + Username: vm.Username, + Password: vm.Password, + Region: vm.Region, + TenantName: vm.TenantName, + FlavorName: vm.FlavorName, + ImageID: vm.ImageID, + ImageMetadata: vm.ImageMetadata, + ImagePath: vm.ImagePath, + Volume: vm.Volume, + InstanceID: vm.InstanceID, + Name: vm.Name, + Networks: vm.Networks, + FloatingIPPool: vm.FloatingIPPool, + FloatingIP: vm.FloatingIP, + SecurityGroup: vm.SecurityGroup, + UserData: vm.UserData, + AdminPassword: vm.AdminPassword, + Credentials: credsAlias{ + SSHUser: vm.Credentials.SSHUser, + SSHPassword: vm.Credentials.SSHPassword, + SSHPrivateKey: vm.Credentials.SSHPrivateKey, + }, + } + + b, err := json.Marshal(alias) + if err != nil { + return nil, err + } + + // We have vm serialized to JSON, but it's missing the floating IP ID. + // Unmarshal it into a map[string]interface{}, add the floating IP ID back + // in, then serialize it again. + var temp interface{} + err = json.Unmarshal(b, &temp) + if err != nil { + return nil, err + } + + m := temp.(map[string]interface{}) + fip := m["FloatingIP"].(map[string]interface{}) + fip["id"] = vm.FloatingIP.ID + + return json.Marshal(m) +} + // GetName returns the name of the virtual machine func (vm *VM) GetName() string { return vm.Name @@ -262,7 +349,7 @@ func (vm *VM) Provision() error { return cleanup(fmt.Errorf("empty floating IP pool")) } - fip, err := floatingip.Create(client, &floatingip.CreateOpts{ + fip, err := floatingips.Create(client, &floatingips.CreateOpts{ Pool: vm.FloatingIPPool, }).Extract() @@ -270,9 +357,9 @@ func (vm *VM) Provision() error { return cleanup(fmt.Errorf("unable to create a floating ip: %s", err)) } - err = floatingip.Associate(client, server.ID, fip.IP).ExtractErr() + err = floatingips.AssociateInstance(client, server.ID, floatingips.AssociateOpts{FloatingIP: fip.IP}).ExtractErr() if err != nil { - errFipDelete := floatingip.Delete(client, fip.ID).ExtractErr() + errFipDelete := floatingips.Delete(client, fip.ID).ExtractErr() err = fmt.Errorf("%s %s", err, errFipDelete) return cleanup(fmt.Errorf("unable to associate a floating ip: %s", err)) } @@ -300,9 +387,7 @@ func (vm *VM) Provision() error { // returns nil if there was an error obtaining the IPs. func (vm *VM) GetIPs() ([]net.IP, error) { server, err := getServer(vm) - if err != nil { - } - if server == nil { + if server == nil || err != nil { // Probably need to call Provision first. return nil, err } @@ -352,11 +437,11 @@ func (vm *VM) Destroy() error { // Delete the floating IP first before destroying the VM var errors []error if vm.FloatingIP != nil { - err = floatingip.Disassociate(client, vm.InstanceID, vm.FloatingIP.IP).ExtractErr() + err = floatingips.DisassociateInstance(client, vm.InstanceID, floatingips.DisassociateOpts{FloatingIP: vm.FloatingIP.IP}).ExtractErr() if err != nil { errors = append(errors, fmt.Errorf("unable to disassociate floating ip from instance: %s", err)) } else { - err = floatingip.Delete(client, vm.FloatingIP.ID).ExtractErr() + err = floatingips.Delete(client, vm.FloatingIP.ID).ExtractErr() if err != nil { errors = append(errors, fmt.Errorf("unable to delete floating ip: %s", err)) } @@ -372,7 +457,7 @@ func (vm *VM) Destroy() error { } // Delete the instance - err = deleteVM(client, vm) + err = deleteVM(client, vm.InstanceID) if err != nil { errors = append(errors, err) } diff --git a/virtualmachine/virtualbox/util.go b/virtualmachine/virtualbox/util.go index e37c4919..70f7261b 100644 --- a/virtualmachine/virtualbox/util.go +++ b/virtualmachine/virtualbox/util.go @@ -155,10 +155,9 @@ func (vm *VM) requestIPs() []net.IP { } func (vm *VM) waitUntilReady() error { - // Check if the vm already has ips before starting the vm - // If it does then wait until the timestamp for at least one of them changes. - var ips []net.IP - ips = vm.requestIPs() + // Check if the VM already has IPs before starting the VM. If it does then + // wait until the timestamp for at least one of the changes. + ips := vm.requestIPs() timestamps := map[string]string{} for k, v := range vm.ipUpdate { timestamps[k] = v diff --git a/virtualmachine/virtualbox/vm.go b/virtualmachine/virtualbox/vm.go index 6b957bb9..9dd742ab 100644 --- a/virtualmachine/virtualbox/vm.go +++ b/virtualmachine/virtualbox/vm.go @@ -40,7 +40,6 @@ type NIC struct { Idx int Backing Backing BackingDevice string - runner Runner } // Runner is an encapsulation around the vmrun utility. @@ -174,7 +173,7 @@ func (vm *VM) GetIPs() ([]net.IP, error) { // GetState gets the power state of the VM being serviced by this driver. func (vm *VM) GetState() (string, error) { - stdout, err := runner.RunCombinedError("showvminfo", fmt.Sprintf("%s", vm.Name)) + stdout, err := runner.RunCombinedError("showvminfo", vm.Name) if err != nil { return "", lvm.WrapErrors(lvm.ErrVMInfoFailed, err) } @@ -193,7 +192,7 @@ func (vm *VM) GetState() (string, error) { // GetInterfaces gets all the network cards attached to this VM func (vm *VM) GetInterfaces() ([]NIC, error) { nics := []NIC{} - stdout, err := runner.RunCombinedError("showvminfo", fmt.Sprintf("%s", vm.Name)) + stdout, err := runner.RunCombinedError("showvminfo", vm.Name) if err != nil { return nil, err } @@ -244,7 +243,7 @@ func (vm *VM) Provision() error { // See comment on mutex definition for details. createMutex.Lock() - _, err = runner.RunCombinedError("import", vm.Src, "--vsys", "0", "--vmname", fmt.Sprintf("%s", vm.Name)) + _, err = runner.RunCombinedError("import", vm.Src, "--vsys", "0", "--vmname", vm.Name) createMutex.Unlock() if err != nil { return err diff --git a/virtualmachine/vmrun/util.go b/virtualmachine/vmrun/util.go index 61f28d1f..b4d4ee2d 100644 --- a/virtualmachine/vmrun/util.go +++ b/virtualmachine/vmrun/util.go @@ -3,6 +3,7 @@ package vmrun import ( + "fmt" "io" "os" "path/filepath" @@ -19,9 +20,17 @@ func copyDir(src string, dest string) error { return err } - dir, _ := os.Open(src) + dir, err := os.Open(src) + if err != nil { + return err + } + defer dir.Close() + // Pass -1 to Readdir to make it read everything into a single slice children, err := dir.Readdir(-1) + if err != nil { + return fmt.Errorf("failed to read files from directory %q: %v", src, err) + } for _, child := range children { newSrc := filepath.Join(src, child.Name()) diff --git a/virtualmachine/vmrun/vm.go b/virtualmachine/vmrun/vm.go index 357517d6..c0d67f47 100644 --- a/virtualmachine/vmrun/vm.go +++ b/virtualmachine/vmrun/vm.go @@ -324,7 +324,11 @@ func (vm *VM) Provision() error { return err } - runVMware() + _, _, err = runVMware() + if err != nil { + return err + } + return vm.waitUntilReady() } diff --git a/virtualmachine/vsphere/util.go b/virtualmachine/vsphere/util.go index 443894bb..f71ff555 100644 --- a/virtualmachine/vsphere/util.go +++ b/virtualmachine/vsphere/util.go @@ -24,6 +24,7 @@ import ( "github.com/vmware/govmomi" "github.com/vmware/govmomi/find" "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/ovf" "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/vim25" "github.com/vmware/govmomi/vim25/mo" @@ -1423,7 +1424,7 @@ var uploadTemplate = func(vm *VM, dcMo *mo.Datacenter, selectedDatastore string) PropertyMapping: nil, } - ovfManager := object.NewOvfManager(vm.client.Client) + ovfManager := ovf.NewManager(vm.client.Client) rpo := object.NewResourcePool(vm.client.Client, l.ResourcePool) specResult, err := ovfManager.CreateImportSpec(vm.ctx, ovfContent, rpo, diff --git a/virtualmachine/vsphere/vm.go b/virtualmachine/vsphere/vm.go index a5756388..bbd018ab 100644 --- a/virtualmachine/vsphere/vm.go +++ b/virtualmachine/vsphere/vm.go @@ -22,8 +22,8 @@ import ( lvm "github.com/apcera/libretto/virtualmachine" "github.com/vmware/govmomi" "github.com/vmware/govmomi/find" + "github.com/vmware/govmomi/nfc" "github.com/vmware/govmomi/object" - "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/soap" "github.com/vmware/govmomi/vim25/types" @@ -64,7 +64,7 @@ func (v vmwareFinder) NetworkList(c context.Context, p string) ([]object.Network } // NewLease creates a VMwareLease. -var NewLease = func(ctx context.Context, lease *object.HttpNfcLease) Lease { +var NewLease = func(ctx context.Context, lease *nfc.Lease) Lease { return VMwareLease{ Ctx: ctx, Lease: lease, @@ -74,23 +74,23 @@ var NewLease = func(ctx context.Context, lease *object.HttpNfcLease) Lease { // VMwareLease implements the Lease interface. type VMwareLease struct { Ctx context.Context - Lease *object.HttpNfcLease + Lease *nfc.Lease } -// HTTPNfcLeaseProgress takes a percentage as an int and sets that percentage as -// the completed percent. -func (v VMwareLease) HTTPNfcLeaseProgress(p int32) { - v.Lease.HttpNfcLeaseProgress(v.Ctx, p) +// Progress takes a percentage as an int and sets that percentage as the +// completed percent. +func (v VMwareLease) Progress(p int) { + v.Lease.Progress(v.Ctx, int32(p)) } // Wait waits for the underlying lease to finish. -func (v VMwareLease) Wait() (*types.HttpNfcLeaseInfo, error) { - return v.Lease.Wait(v.Ctx) +func (v VMwareLease) Wait() (*nfc.LeaseInfo, error) { + return v.Lease.Wait(v.Ctx, nil) } // Complete marks the underlying lease as complete. func (v VMwareLease) Complete() error { - return v.Lease.HttpNfcLeaseComplete(v.Ctx) + return v.Lease.Complete(v.Ctx) } type Datastore struct { @@ -258,7 +258,7 @@ func (r ReadProgress) StartProgress() { r.wg.Add(1) go func() { var bytesReceived int64 - var percent int32 + var percent int tick := time.NewTicker(5 * time.Second) defer tick.Stop() defer r.wg.Done() @@ -266,10 +266,10 @@ func (r ReadProgress) StartProgress() { select { case b := <-r.ch: bytesReceived += b - percent = int32((float32(bytesReceived) / float32(r.TotalBytes)) * 100) + percent = int((float32(bytesReceived) / float32(r.TotalBytes)) * 100) case <-tick.C: // TODO: Preet This can return an error as well, should return it - r.Lease.HTTPNfcLeaseProgress(percent) + r.Lease.Progress(percent) if percent == 100 { return } @@ -443,18 +443,6 @@ type finder interface { SetDatacenter(*object.Datacenter) *find.Finder } -type vmwareCollector struct { - collector *property.Collector -} - -func (v vmwareCollector) RetrieveOne(c context.Context, mor types.ManagedObjectReference, ps []string, dst interface{}) error { - return v.collector.RetrieveOne(c, mor, ps, dst) -} - -func (v vmwareCollector) Retrieve(c context.Context, mor []types.ManagedObjectReference, ps []string, dst interface{}) error { - return v.collector.Retrieve(c, mor, ps, dst) -} - type location struct { Host types.ManagedObjectReference ResourcePool types.ManagedObjectReference @@ -473,10 +461,10 @@ type Destination struct { HostSystem string } -// Lease represents a type that wraps around a HTTPNfcLease +// Lease represents a type that wraps around a nfc.Lease type Lease interface { - HTTPNfcLeaseProgress(int32) - Wait() (*types.HttpNfcLeaseInfo, error) + Progress(int) + Wait() (*nfc.LeaseInfo, error) Complete() error } @@ -580,7 +568,10 @@ func (vm *VM) Provision() (err error) { } // Cancel the sdk context - defer vm.cancel() + defer func() { + vm.client.Logout(vm.ctx) + vm.cancel() + }() // Get a reference to the datacenter with host and vm folders populated dcMo, err := GetDatacenter(vm) @@ -791,7 +782,10 @@ func (vm *VM) GetIPsAndId() ([]net.IP, string, error) { if err := SetupSession(vm); err != nil { return nil, "", err } - defer vm.cancel() + defer func() { + vm.client.Logout(vm.ctx) + vm.cancel() + }() // Get a reference to the datacenter with host and vm folders populated dcMo, err := GetDatacenter(vm) @@ -818,7 +812,10 @@ func (vm *VM) Destroy() (err error) { if err := SetupSession(vm); err != nil { return err } - defer vm.cancel() + defer func() { + vm.client.Logout(vm.ctx) + vm.cancel() + }() // Get a reference to the datacenter with host and vm folders populated dcMo, err := GetDatacenter(vm) @@ -998,8 +995,10 @@ func (vm *VM) GetState() (state string, err error) { if err := SetupSession(vm); err != nil { return "", lvm.ErrVMInfoFailed } - defer vm.cancel() - + defer func() { + vm.client.Logout(vm.ctx) + vm.cancel() + }() state, err = getState(vm) if err != nil { return "", err @@ -1021,7 +1020,11 @@ func (vm *VM) Suspend() (err error) { if err := SetupSession(vm); err != nil { return err } - defer vm.cancel() + + defer func() { + vm.client.Logout(vm.ctx) + vm.cancel() + }() // Get a reference to the datacenter with host and vm folders populated dcMo, err := GetDatacenter(vm) @@ -1052,7 +1055,11 @@ func (vm *VM) Halt() (err error) { if err := SetupSession(vm); err != nil { return err } - defer vm.cancel() + defer func() { + vm.client.Logout(vm.ctx) + vm.cancel() + }() + return halt(vm) } @@ -1079,7 +1086,11 @@ func (vm *VM) Start() (err error) { if err := SetupSession(vm); err != nil { return err } - defer vm.cancel() + defer func() { + vm.client.Logout(vm.ctx) + vm.cancel() + }() + return start(vm) } diff --git a/virtualmachine/vsphere/vm_test.go b/virtualmachine/vsphere/vm_test.go index 70682780..292eb6c9 100644 --- a/virtualmachine/vsphere/vm_test.go +++ b/virtualmachine/vsphere/vm_test.go @@ -3,6 +3,7 @@ package vsphere import ( + "context" "errors" "fmt" "io" @@ -12,10 +13,9 @@ import ( "strings" "testing" - "golang.org/x/net/context" - "github.com/apcera/libretto/virtualmachine" "github.com/vmware/govmomi" + "github.com/vmware/govmomi/nfc" "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/vim25" @@ -58,11 +58,11 @@ type mockCollector struct { type mockLease struct { MockLeaseProgress func(p int) - MockWait func() (*types.HttpNfcLeaseInfo, error) + MockWait func() (*nfc.LeaseInfo, error) MockComplete func() error } -func (m mockLease) HTTPNfcLeaseProgress(p int) { +func (m mockLease) Progress(p int) { if m.MockLeaseProgress != nil { m.MockLeaseProgress(p) } @@ -75,7 +75,7 @@ func (m mockLease) Complete() error { return nil } -func (m mockLease) Wait() (*types.HttpNfcLeaseInfo, error) { +func (m mockLease) Wait() (*nfc.LeaseInfo, error) { if m.MockWait != nil { return m.MockWait() } @@ -547,7 +547,7 @@ func TestResetUnitNumbers(t *testing.T) { vmSpec.ConfigSpec.DeviceChange = []types.BaseVirtualDeviceConfigSpec{ &types.VirtualDeviceConfigSpec{ Device: &types.VirtualDevice{ - UnitNumber: 0, + UnitNumber: new(int32), }, }, } @@ -558,14 +558,14 @@ func TestResetUnitNumbers(t *testing.T) { if len(s.DeviceChange) != 1 { t.Fatalf("Expected only one device, got: %d", len(s.DeviceChange)) } - if n := s.DeviceChange[0].GetVirtualDeviceConfigSpec().Device.GetVirtualDevice().UnitNumber; n != -1 { + if n := s.DeviceChange[0].GetVirtualDeviceConfigSpec().Device.GetVirtualDevice().UnitNumber; *n != -1 { t.Fatalf("Expected to get -1 for the unit number, got: %d", n) } } func TestUploadOvfLeaseWaitError(t *testing.T) { l := mockLease{ - MockWait: func() (*types.HttpNfcLeaseInfo, error) { + MockWait: func() (*nfc.LeaseInfo, error) { return nil, fmt.Errorf("Error waiting on the nfc lease") }, } @@ -577,133 +577,6 @@ func TestUploadOvfLeaseWaitError(t *testing.T) { } } -func TestUploadOvfOpenError(t *testing.T) { - l := mockLease{ - MockWait: func() (*types.HttpNfcLeaseInfo, error) { - li := types.HttpNfcLeaseInfo{ - DeviceUrl: []types.HttpNfcLeaseDeviceUrl{ - { - Url: "http://*/", - }, - }, - } - return &li, nil - }, - } - var oldOpen = open - defer func() { - open = oldOpen - }() - expectedError := "failed to open file" - open = func(name string) (file *os.File, err error) { - return nil, fmt.Errorf(expectedError) - } - vm := VM{} - sr := types.OvfCreateImportSpecResult{ - FileItem: []types.OvfFileItem{ - {}, - }, - } - err := uploadOvf(&vm, &sr, l) - if err.Error() != expectedError { - t.Fatalf("Expected to get an error %s, got: %s", expectedError, err) - } -} - -func TestUploadOvfCreateRequestError(t *testing.T) { - l := mockLease{ - MockWait: func() (*types.HttpNfcLeaseInfo, error) { - li := types.HttpNfcLeaseInfo{ - DeviceUrl: []types.HttpNfcLeaseDeviceUrl{ - { - Url: "http://*/", - }, - }, - } - return &li, nil - }, - } - fileName := "test" - var oldOpen = open - var oldCreateRequest = createRequest - defer func() { - open = oldOpen - createRequest = oldCreateRequest - }() - expectedError := "failed to create request" - open = func(name string) (file *os.File, err error) { - return os.Create(fileName) - } - createRequest = func(r io.Reader, method string, insecure bool, length int64, url string, contentType string) error { - return fmt.Errorf(expectedError) - } - defer func() { - err := os.RemoveAll(fileName) - if err != nil { - panic("Unable to remove temp file for test") - } - }() - vm := VM{} - sr := types.OvfCreateImportSpecResult{ - FileItem: []types.OvfFileItem{ - {}, - }, - } - err := uploadOvf(&vm, &sr, l) - if err.Error() != expectedError { - t.Fatalf("Expected to get an error %s, got: %s", expectedError, err) - } -} - -func TestUploadOvfHappyPath(t *testing.T) { - l := mockLease{ - MockWait: func() (*types.HttpNfcLeaseInfo, error) { - li := types.HttpNfcLeaseInfo{ - DeviceUrl: []types.HttpNfcLeaseDeviceUrl{ - { - Url: "http://*/", - }, - }, - } - return &li, nil - }, - } - fileName := "test" - var oldOpen = open - var oldCreateRequest = createRequest - var oldNewProgressReader = NewProgressReader - defer func() { - open = oldOpen - createRequest = oldCreateRequest - NewProgressReader = oldNewProgressReader - }() - open = func(name string) (file *os.File, err error) { - return os.Create(fileName) - } - createRequest = func(r io.Reader, method string, insecure bool, length int64, url string, contentType string) error { - return nil - } - NewProgressReader = func(r io.Reader, t int64, l Lease) ProgressReader { - return mockProgressReader{} - } - defer func() { - err := os.RemoveAll(fileName) - if err != nil { - panic("Unable to remove temp file for test") - } - }() - vm := VM{} - sr := types.OvfCreateImportSpecResult{ - FileItem: []types.OvfFileItem{ - {}, - }, - } - err := uploadOvf(&vm, &sr, l) - if err != nil { - t.Fatalf("Expected to get no error, got: %s", err) - } -} - func TestCreateRequestNewRequestError(t *testing.T) { errProtocol := `unsupported protocol scheme ""` err := createRequest(mockProgressReader{}, "foo", true, 0, "", "foo")