Merge pull request #66795 from jiatongw/zones_vendor

Automatic merge from submit-queue (batch tested with PRs 67052, 67094, 66795). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Add zones support for vSphere cloud provider(in-tree)

**What this PR does / why we need it**:
This PR added zones(built-in node labels) support for vSphere cloud provider(in-tree).  More details can be found in the issue as below.

**Which issue(s) this PR fixes** :
Partially fixes phase 1 of issue #64021 

**Special notes for your reviewer**:

**Release note**:

```release-note
NONE
```
pull/8/head
Kubernetes Submit Queue 2018-08-07 17:16:04 -07:00 committed by GitHub
commit 9d260ff163
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 2180 additions and 291 deletions

271
Godeps/Godeps.json generated
View File

@ -1036,162 +1036,162 @@
},
{
"ImportPath": "github.com/docker/distribution/digestset",
"Comment": "v2.6.0-rc.1-209-gedc3ab2",
"Comment": "v2.6.0-rc.1-209-gedc3ab29",
"Rev": "edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c"
},
{
"ImportPath": "github.com/docker/distribution/reference",
"Comment": "v2.6.0-rc.1-209-gedc3ab2",
"Comment": "v2.6.0-rc.1-209-gedc3ab29",
"Rev": "edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c"
},
{
"ImportPath": "github.com/docker/docker/api",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/blkiodev",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/container",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/events",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/filters",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/image",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/mount",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/network",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/registry",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/strslice",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/swarm",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/swarm/runtime",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/time",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/versions",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/api/types/volume",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/client",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/ioutils",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/jsonlog",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/jsonmessage",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/longpath",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/mount",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/parsers",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/parsers/operatingsystem",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/stdcopy",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/sysinfo",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/system",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/term",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/term/windows",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
"ImportPath": "github.com/docker/docker/pkg/tlsconfig",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616f",
"Comment": "docs-v1.12.0-rc4-2016-07-15-7401-g4f3616fb1",
"Rev": "4f3616fb1c112e206b88cb7a9922bf49067a7756"
},
{
@ -1216,7 +1216,7 @@
},
{
"ImportPath": "github.com/docker/libnetwork/ipvs",
"Comment": "v0.8.0-dev.2-910-gba46b92",
"Comment": "v0.8.0-dev.2-910-gba46b928",
"Rev": "ba46b928444931e6865d8618dc03622cac79aa6f"
},
{
@ -1334,132 +1334,132 @@
},
{
"ImportPath": "github.com/gogo/protobuf/gogoproto",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/compare",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/defaultcheck",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/description",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/embedcheck",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/enumstringer",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/equal",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/face",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/gostring",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/marshalto",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/oneofcheck",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/populate",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/size",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/stringer",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/testgen",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/union",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/plugin/unmarshal",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/proto",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/protoc-gen-gogo/descriptor",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/protoc-gen-gogo/generator",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/protoc-gen-gogo/grpc",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/protoc-gen-gogo/plugin",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/sortkeys",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/types",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/vanity",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
"ImportPath": "github.com/gogo/protobuf/vanity/command",
"Comment": "v0.4-3-gc0656ed",
"Comment": "v0.4-3-gc0656edd",
"Rev": "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
},
{
@ -2358,82 +2358,82 @@
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/apparmor",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/fs",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/systemd",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/configs",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/configs/validate",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/criurpc",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/intelrdt",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/keys",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/mount",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/seccomp",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/stacktrace",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/system",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/user",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/utils",
"Comment": "v1.0.0-rc5-46-g871ba2e",
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
@ -2673,148 +2673,153 @@
},
{
"ImportPath": "github.com/vmware/govmomi",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/find",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/list",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/lookup",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/lookup/methods",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/lookup/simulator",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/lookup/types",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/nfc",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/object",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/pbm",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/pbm/methods",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/pbm/types",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/property",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/session",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/simulator",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/simulator/esx",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/simulator/vpx",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/sts",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/sts/internal",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/sts/simulator",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/task",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/vapi/tags",
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/debug",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/methods",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/mo",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/progress",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/soap",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/types",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/xml",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
"Comment": "v0.18.0-40-gbbd9953",
"Rev": "bbd99532a768d2fe369079ceda730e30726ae1a6"
},
{
"ImportPath": "github.com/vmware/photon-controller-go-sdk/SSPI",

210
Godeps/LICENSES generated
View File

@ -88165,6 +88165,216 @@ SOFTWARE.
================================================================================
================================================================================
= vendor/github.com/vmware/govmomi/vapi/tags licensed under: =
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
= vendor/github.com/vmware/govmomi/LICENSE.txt 3b83ef96387f14655fc854ddc3c6bd57
================================================================================
================================================================================
= vendor/github.com/vmware/govmomi/vim25 licensed under: =

View File

@ -29,6 +29,7 @@ go_library(
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/vmware/govmomi/vapi/tags:go_default_library",
"//vendor/github.com/vmware/govmomi/vim25:go_default_library",
"//vendor/github.com/vmware/govmomi/vim25/mo:go_default_library",
"//vendor/gopkg.in/gcfg.v1:go_default_library",

View File

@ -82,6 +82,21 @@ func (dc *Datacenter) GetVMByUUID(ctx context.Context, vmUUID string) (*VirtualM
return &virtualMachine, nil
}
// GetHostByVMUUID gets the host object from the given vmUUID
func (dc *Datacenter) GetHostByVMUUID(ctx context.Context, vmUUID string) (*types.ManagedObjectReference, error) {
virtualMachine, err := dc.GetVMByUUID(ctx, vmUUID)
var vmMo mo.VirtualMachine
pc := property.DefaultCollector(virtualMachine.Client())
err = pc.RetrieveOne(ctx, virtualMachine.Reference(), []string{"summary.runtime.host"}, &vmMo)
if err != nil {
glog.Errorf("Failed to retrive VM runtime host, err: %v", err)
return nil, err
}
host := vmMo.Summary.Runtime.Host
glog.Infof("%s host is %s", virtualMachine.Reference(), host)
return host, nil
}
// GetVMByPath gets the VM object from the given vmPath
// vmPath should be the full path to VM and not just the name
func (dc *Datacenter) GetVMByPath(ctx context.Context, vmPath string) (*VirtualMachine, error) {

View File

@ -22,6 +22,7 @@ import (
"fmt"
"io"
"net"
"net/url"
"os"
"path"
"path/filepath"
@ -33,6 +34,7 @@ import (
"gopkg.in/gcfg.v1"
"github.com/golang/glog"
"github.com/vmware/govmomi/vapi/tags"
"k8s.io/api/core/v1"
k8stypes "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/informers"
@ -177,6 +179,12 @@ type VSphereConfig struct {
DefaultDatastore string `gcfg:"default-datastore"`
ResourcePoolPath string `gcfg:"resourcepool-path"`
}
// Tag categories and tags which correspond to "built-in node labels: zones and region"
Labels struct {
Zone string `gcfg:"zone"`
Region string `gcfg:"region"`
}
}
type Volumes interface {
@ -808,8 +816,11 @@ func (vs *VSphere) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
// Zones returns an implementation of Zones for vSphere.
func (vs *VSphere) Zones() (cloudprovider.Zones, bool) {
glog.V(1).Info("The vSphere cloud provider does not support zones")
return nil, false
if vs.cfg == nil {
glog.V(1).Info("The vSphere cloud provider does not support zones")
return nil, false
}
return vs, true
}
// Routes returns a false since the interface is not supported for vSphere.
@ -1306,3 +1317,99 @@ func (vs *VSphere) NodeManager() (nodeManager *NodeManager) {
}
return vs.nodeManager
}
func withTagsClient(ctx context.Context, connection *vclib.VSphereConnection, f func(c *tags.RestClient) error) error {
vsURL := connection.Client.URL()
vsURL.User = url.UserPassword(connection.Username, connection.Password)
c := tags.NewClient(vsURL, connection.Insecure, "")
if err := c.Login(ctx); err != nil {
return err
}
defer c.Logout(ctx)
return f(c)
}
// GetZone implements Zones.GetZone
func (vs *VSphere) GetZone(ctx context.Context) (cloudprovider.Zone, error) {
nodeName, err := vs.CurrentNodeName(ctx, vs.hostName)
if err != nil {
glog.Errorf("Cannot get node name.")
return cloudprovider.Zone{}, err
}
zone := cloudprovider.Zone{}
vsi, err := vs.getVSphereInstanceForServer(vs.cfg.Workspace.VCenterIP, ctx)
if err != nil {
glog.Errorf("Cannot connent to vsphere. Get zone for node %s error", nodeName)
return cloudprovider.Zone{}, err
}
dc, err := vclib.GetDatacenter(ctx, vsi.conn, vs.cfg.Workspace.Datacenter)
if err != nil {
glog.Errorf("Cannot connent to datacenter. Get zone for node %s error", nodeName)
return cloudprovider.Zone{}, err
}
vmHost, err := dc.GetHostByVMUUID(ctx, vs.vmUUID)
if err != nil {
glog.Errorf("Cannot find VM runtime host. Get zone for node %s error", nodeName)
return cloudprovider.Zone{}, err
}
client := vsi.conn
err = withTagsClient(ctx, client, func(client *tags.RestClient) error {
tags, err := client.ListAttachedTags(ctx, vmHost)
if err != nil {
glog.Errorf("Cannot list attached tags. Get zone for node %s error", nodeName)
return err
}
for _, value := range tags {
tag, err := client.GetTag(ctx, value)
if err != nil {
glog.Errorf("Get tag %s error", value)
return err
}
category, err := client.GetCategory(ctx, tag.CategoryID)
if err != nil {
glog.Errorf("Get category %s error", value)
return err
}
switch {
case category.Name == vs.cfg.Labels.Zone:
zone.FailureDomain = tag.Name
case category.Name == vs.cfg.Labels.Region:
zone.Region = tag.Name
default:
zone.FailureDomain = ""
zone.Region = ""
}
}
switch {
case zone.Region == "":
if vs.cfg.Labels.Zone != "" {
return fmt.Errorf("The zone in vSphere configuration file not match for node %s ", nodeName)
}
glog.Infof("No zones support for node %s error", nodeName)
return nil
case zone.FailureDomain == "":
if vs.cfg.Labels.Region != "" {
return fmt.Errorf("The zone in vSphere configuration file not match for node %s ", nodeName)
}
glog.Infof("No zones support for node %s error", nodeName)
return nil
}
return nil
})
if err != nil {
glog.Errorf("Get zone for node %s error", nodeName)
return cloudprovider.Zone{}, err
}
return zone, nil
}
func (vs *VSphere) GetZoneByNodeName(ctx context.Context, nodeName k8stypes.NodeName) (cloudprovider.Zone, error) {
return cloudprovider.Zone{}, cloudprovider.NotImplemented
}
func (vs *VSphere) GetZoneByProviderID(ctx context.Context, providerID string) (cloudprovider.Zone, error) {
return cloudprovider.Zone{}, cloudprovider.NotImplemented
}

View File

@ -315,17 +315,21 @@ func TestVSphereLoginWithCaCert(t *testing.T) {
}
func TestZones(t *testing.T) {
cfg := VSphereConfig{}
cfg.Global.Datacenter = "myDatacenter"
// Create vSphere configuration object
cfg, ok := configFromEnv()
vs := VSphere{
cfg: &cfg,
}
_, ok := vs.Zones()
if ok {
t.Fatalf("Zones() returned true")
if !ok {
t.Skipf("No config found in environment")
}
_, err := vs.GetZone(context.TODO())
if err != nil {
t.Fatalf("GetZone() failed: %s", err)
}
_, ok = vs.Zones()
if !ok {
t.Fatalf("Zones() returned false")
}
}

View File

@ -7,6 +7,7 @@ Cédric Blomart <cblomart@gmail.com> cedric <cblomart@gmail.com>
David Stark <dave@davidstark.name> <david.stark@bskyb.com>
Eric Gray <egray@vmware.com> <ericgray@users.noreply.github.com>
Eric Yutao <eric.yutao@gmail.com> eric <eric.yutao@gmail.com>
Fabio Rapposelli <fabio@vmware.com> <fabio@rapposelli.org>
Henrik Hodne <henrik@travis-ci.com> <henrik@hodne.io>
Jeremy Canady <jcanady@jackhenry.com> <jcanady@gmail.com>
Pieter Noordhuis <pnoordhuis@vmware.com> <pcnoordhuis@gmail.com>

View File

@ -1,10 +1,8 @@
sudo: false
sudo: required
language: go
go:
- 1.8.x
- 1.9.x
- '1.10'
go_import_path: github.com/vmware/govmomi
@ -27,4 +25,4 @@ deploy:
on:
tags: true
condition: $TRAVIS_OS_NAME = linux
go: '1.10'
go: '1.10'

View File

@ -37,6 +37,7 @@ filegroup(
"//vendor/github.com/vmware/govmomi/simulator:all-srcs",
"//vendor/github.com/vmware/govmomi/sts:all-srcs",
"//vendor/github.com/vmware/govmomi/task:all-srcs",
"//vendor/github.com/vmware/govmomi/vapi/tags:all-srcs",
"//vendor/github.com/vmware/govmomi/vim25:all-srcs",
],
tags = ["automanaged"],

View File

@ -1,5 +1,31 @@
# changelog
### unreleased
* SetRootCAs on the soap.Client returns an error for invalid certificates
* Add ClusterComputeResource.MoveInto method
### 0.18.0 (2018-05-24)
* Add VirtualDiskManager wrapper to set UUID
* Add vmxnet2, pcnet32 and sriov to VirtualDeviceList.EthernetCardTypes
* Add new vSphere 6.7 APIs
* Decrease LoginExtensionByCertificate tunnel usage
* SAML token authentication support via SessionManager.LoginByToken
* New SSO admin client for managing users
* New STS client for issuing and renewing SAML tokens
* New Lookup Service client for discovering endpoints such as STS and ssoadmin
* Switch from gvt to go dep for managing dependencies
### 0.17.1 (2018-03-19)
* vcsim: add Destroy method for Folder and Datacenter types

View File

@ -6,9 +6,11 @@
Abhijeet Kasurde <akasurde@redhat.com>
abrarshivani <abrarshivani@users.noreply.github.com>
Adam Shannon <adamkshannon@gmail.com>
akutz <sakutz@gmail.com>
Alessandro Cortiana <alessandro.cortiana@gmail.com>
Alex Bozhenko <alexbozhenko@fb.com>
Alvaro Miranda <kikitux@gmail.com>
amandahla <amanda.andrade@serpro.gov.br>
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br>
Amit Bathla <abathla@.vmware.com>
amit bezalel <amit.bezalel@hpe.com>
@ -23,14 +25,15 @@ bastienbc <bastien.barbe.creuly@gmail.com>
Bob Killen <killen.bob@gmail.com>
Brad Fitzpatrick <bradfitz@golang.org>
Bruce Downs <bruceadowns@gmail.com>
Cédric Blomart <cblomart@gmail.com>
Chris Marchesi <chrism@vancluevertech.com>
Christian Höltje <docwhat@gerf.org>
Clint Greenwood <cgreenwood@vmware.com>
Cédric Blomart <cblomart@gmail.com>
Danny Lockard <danny.lockard@banno.com>
Dave Tucker <dave@dtucker.co.uk>
David Stark <dave@davidstark.name>
Davide Agnello <dagnello@hp.com>
David Stark <dave@davidstark.name>
Deric Crago <deric.crago@gmail.com>
Doug MacEachern <dougm@vmware.com>
Eloy Coto <eloy.coto@gmail.com>
Eric Gray <egray@vmware.com>
@ -52,11 +55,14 @@ Jason Kincl <jkincl@gmail.com>
Jeremy Canady <jcanady@jackhenry.com>
jeremy-clerc <jeremy@clerc.io>
João Pereira <joaodrp@gmail.com>
Jorge Sevilla <jorge.sevilla@rstor.io>
leslie-qiwa <leslie.qiwa@gmail.com>
Louie Jiang <jiangl@vmware.com>
Marc Carmier <mcarmier@gmail.com>
Matthew Cosgrove <matthew.cosgrove@dell.com>
Mevan Samaratunga <mevansam@gmail.com>
Nicolas Lamirault <nicolas.lamirault@gmail.com>
Omar Kohl <omarkohl@gmail.com>
Parham Alvani <parham.alvani@gmail.com>
Pieter Noordhuis <pnoordhuis@vmware.com>
runner.mei <runner.mei@gmail.com>

View File

@ -18,7 +18,7 @@ install:
go install -v github.com/vmware/govmomi/vcsim
go-test:
go test -race -v $(TEST_OPTS) ./...
GORACE=history_size=5 go test -timeout 5m -count 1 -race -v $(TEST_OPTS) ./...
govc-test: install
(cd govc/test && ./vendor/github.com/sstephenson/bats/libexec/bats -t .)

View File

@ -15,9 +15,9 @@ In addition to the vSphere API client, this repository includes:
## Compatibility
This library is built for and tested against ESXi and vCenter 6.0 and 6.5.
This library is built for and tested against ESXi and vCenter 6.0, 6.5 and 6.7.
It should work with versions 5.5 and 5.1, but neither are officially supported.
It may work with versions 5.5 and 5.1, but neither are officially supported.
## Documentation

View File

@ -68,3 +68,22 @@ func (c ClusterComputeResource) AddHost(ctx context.Context, spec types.HostConn
return NewTask(c.c, res.Returnval), nil
}
func (c ClusterComputeResource) MoveInto(ctx context.Context, hosts ...*HostSystem) (*Task, error) {
req := types.MoveInto_Task{
This: c.Reference(),
}
hostReferences := make([]types.ManagedObjectReference, len(hosts))
for i, host := range hosts {
hostReferences[i] = host.Reference()
}
req.Host = hostReferences
res, err := methods.MoveInto_Task(ctx, c.c, &req)
if err != nil {
return nil, err
}
return NewTask(c.c, res.Returnval), nil
}

View File

@ -209,3 +209,19 @@ func (m VirtualDiskManager) QueryVirtualDiskUuid(ctx context.Context, name strin
return res.Returnval, nil
}
func (m VirtualDiskManager) SetVirtualDiskUuid(ctx context.Context, name string, dc *Datacenter, uuid string) error {
req := types.SetVirtualDiskUuid{
This: m.Reference(),
Name: name,
Uuid: uuid,
}
if dc != nil {
ref := dc.Reference()
req.Datacenter = &ref
}
_, err := methods.SetVirtualDiskUuid(ctx, m.c, &req)
return err
}

View File

@ -799,3 +799,16 @@ func (v VirtualMachine) UpgradeVM(ctx context.Context, version string) (*Task, e
return NewTask(v.c, res.Returnval), nil
}
// UUID is a helper to get the UUID of the VirtualMachine managed object.
// This method returns an empty string if an error occurs when retrieving UUID from the VirtualMachine object.
func (v VirtualMachine) UUID(ctx context.Context) string {
var o mo.VirtualMachine
err := v.Properties(ctx, v.Reference(), []string{"config.uuid"}, &o)
if err != nil {
return ""
}
return o.Config.Uuid
}

View File

@ -111,6 +111,12 @@ func (p *Collector) WaitForUpdates(ctx context.Context, v string) (*types.Update
return res.Returnval, nil
}
func (p *Collector) CancelWaitForUpdates(ctx context.Context) error {
req := &types.CancelWaitForUpdates{This: p.Reference()}
_, err := methods.CancelWaitForUpdates(ctx, p.roundTripper, req)
return err
}
func (p *Collector) RetrieveProperties(ctx context.Context, req types.RetrieveProperties) (*types.RetrievePropertiesResponse, error) {
req.This = p.Reference()
return methods.RetrieveProperties(ctx, p.roundTripper, &req)

View File

@ -19,12 +19,14 @@ package property
import (
"context"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
)
// WaitFilter provides helpers to construct a types.CreateFilter for use with property.Wait
type WaitFilter struct {
types.CreateFilter
Options *types.WaitOptions
}
// Add a new ObjectSpec and PropertySpec to the WaitFilter
@ -75,6 +77,7 @@ func Wait(ctx context.Context, c *Collector, obj types.ManagedObjectReference, p
// creates a new property collector and calls CreateFilter. A new property
// collector is required because filters can only be added, not removed.
//
// If the Context is canceled, a call to CancelWaitForUpdates() is made and its error value is returned.
// The newly created collector is destroyed before this function returns (both
// in case of success or error).
//
@ -85,7 +88,7 @@ func WaitForUpdates(ctx context.Context, c *Collector, filter *WaitFilter, f fun
}
// Attempt to destroy the collector using the background context, as the
// specified context may have timed out or have been cancelled.
// specified context may have timed out or have been canceled.
defer p.Destroy(context.Background())
err = p.CreateFilter(ctx, filter.CreateFilter)
@ -93,20 +96,33 @@ func WaitForUpdates(ctx context.Context, c *Collector, filter *WaitFilter, f fun
return err
}
for version := ""; ; {
res, err := p.WaitForUpdates(ctx, version)
req := types.WaitForUpdatesEx{
This: p.Reference(),
Options: filter.Options,
}
for {
res, err := methods.WaitForUpdatesEx(ctx, p.roundTripper, &req)
if err != nil {
if ctx.Err() == context.Canceled {
werr := p.CancelWaitForUpdates(context.Background())
return werr
}
return err
}
// Retry if the result came back empty
if res == nil {
set := res.Returnval
if set == nil {
if req.Options != nil && req.Options.MaxWaitSeconds != nil {
return nil // WaitOptions.MaxWaitSeconds exceeded
}
// Retry if the result came back empty
continue
}
version = res.Version
req.Version = set.Version
for _, fs := range res.FilterSet {
for _, fs := range set.FilterSet {
if f(fs.ObjectSet) {
return nil
}

View File

@ -106,11 +106,10 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
switch types.ConfigSpecOperation(member.Operation) {
case types.ConfigSpecOperationAdd:
if FindReference(host.Network, s.Self) != nil {
if FindReference(s.Summary.HostMember, member.Host) != nil {
return nil, &types.AlreadyExists{Name: host.Name}
}
Map.AppendReference(host, &host.Network, s.Self)
Map.AppendReference(host, &host.Network, s.Portgroup...)
s.Summary.HostMember = append(s.Summary.HostMember, member.Host)
@ -129,8 +128,7 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
}
}
Map.RemoveReference(host, &host.Network, s.Self)
RemoveReference(&s.Summary.HostMember, s.Self)
RemoveReference(&s.Summary.HostMember, member.Host)
case types.ConfigSpecOperationEdit:
return nil, &types.NotSupported{}
}

View File

@ -161,6 +161,12 @@ var EventInfo = []types.EventDescriptionEventDetail{
Category: "info",
FullFormat: "{{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} is starting",
},
{
Key: "VmStoppingEvent",
Description: "VM stopping",
Category: "info",
FullFormat: "{{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} is stopping",
},
{
Key: "VmSuspendingEvent",
Description: "VM being suspended",

View File

@ -157,15 +157,18 @@ func (m *EventManager) PostEvent(ctx *Context, req *types.PostEvent) soap.HasFau
event.CreatedTime = time.Now()
event.UserName = ctx.Session.UserName
m.page = m.page.Next()
m.page = m.page.Prev()
m.page.Value = req.EventToPost
m.formatMessage(req.EventToPost)
for _, c := range m.collectors {
if c.eventMatches(req.EventToPost) {
c.page = c.page.Next()
c.page.Value = event
}
ctx.WithLock(c, func() {
if c.eventMatches(req.EventToPost) {
c.page = c.page.Prev()
c.page.Value = req.EventToPost
Map.Update(c, []types.PropertyChange{{Name: "latestPage", Val: c.GetLatestPage()}})
}
})
}
return &methods.PostEventBody{

View File

@ -29,12 +29,10 @@ func (ds *Datastore) stat() error {
return err
}
bsize := uint64(stat.Bsize) / 512
info.FreeSpace = int64(stat.Bfree*bsize) >> 1
info.FreeSpace = int64(stat.Bfree * uint64(stat.Bsize))
ds.Summary.FreeSpace = info.FreeSpace
ds.Summary.Capacity = int64(stat.Blocks*bsize) >> 1
ds.Summary.Capacity = int64(stat.Blocks * uint64(stat.Bsize))
return nil
}

View File

@ -17,11 +17,14 @@ limitations under the License.
package simulator
import (
"context"
"errors"
"log"
"path"
"reflect"
"strings"
"sync"
"time"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
@ -32,6 +35,11 @@ import (
type PropertyCollector struct {
mo.PropertyCollector
nopLocker
updates []types.ObjectUpdate
mu sync.Mutex
cancel context.CancelFunc
}
func NewPropertyCollector(ref types.ManagedObjectReference) object.Reference {
@ -72,6 +80,10 @@ func getObject(ctx *Context, ref types.ManagedObjectReference) (reflect.Value, b
obj = o.Get()
}
return getManagedObject(obj), true
}
func getManagedObject(obj mo.Reference) reflect.Value {
rval := reflect.ValueOf(obj).Elem()
rtype := rval.Type()
@ -82,26 +94,21 @@ func getObject(ctx *Context, ref types.ManagedObjectReference) (reflect.Value, b
// 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 {
if path.Base(rtype.PkgPath()) == "mo" {
break
}
if rtype.Kind() != reflect.Struct || rtype.NumField() == 0 {
log.Panicf("%#v does not have an embedded mo type", obj.Reference())
}
rval = rval.Field(0)
rtype = rval.Type()
}
return rval, true
return rval
}
func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{} {
if rval.Kind() == reflect.Ptr {
rval = rval.Elem()
}
// wrapValue converts slice types to the appropriate ArrayOf type used in property collector responses.
func wrapValue(rval reflect.Value, rtype reflect.Type) interface{} {
pval := rval.Interface()
if rval.Kind() == reflect.Slice {
@ -128,7 +135,7 @@ func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{}
Long: v,
}
default:
kind := f.Type.Elem().Name()
kind := rtype.Elem().Name()
// Remove govmomi interface prefix name
if strings.HasPrefix(kind, "Base") {
kind = kind[4:]
@ -143,6 +150,14 @@ func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{}
return pval
}
func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{} {
if rval.Kind() == reflect.Ptr {
rval = rval.Elem()
}
return wrapValue(rval, f.Type)
}
func fieldValue(rval reflect.Value, p string) (interface{}, error) {
var value interface{}
fields := strings.Split(p, ".")
@ -401,7 +416,9 @@ func (pc *PropertyCollector) collect(ctx *Context, r *types.RetrievePropertiesEx
// Select object references
for _, spec := range r.SpecSet {
for _, o := range spec.ObjectSet {
rval, ok := getObject(ctx, o.Obj)
var rval reflect.Value
ok := false
ctx.WithLock(o.Obj, func() { rval, ok = getObject(ctx, o.Obj) })
if !ok {
if isFalse(spec.ReportMissingObjectsInResults) {
return nil, &types.ManagedObjectNotFound{Obj: o.Obj}
@ -420,7 +437,7 @@ func (pc *PropertyCollector) collect(ctx *Context, r *types.RetrievePropertiesEx
}
for _, ref := range refs {
rr.collect(ctx, ref)
ctx.WithLock(ref, func() { rr.collect(ctx, ref) })
}
return rr.RetrieveResult, nil
@ -429,7 +446,10 @@ func (pc *PropertyCollector) collect(ctx *Context, r *types.RetrievePropertiesEx
func (pc *PropertyCollector) CreateFilter(ctx *Context, c *types.CreateFilter) soap.HasFault {
body := &methods.CreateFilterBody{}
filter := &PropertyFilter{pc: pc}
filter := &PropertyFilter{
pc: pc,
refs: make(map[types.ManagedObjectReference]struct{}),
}
filter.PartialUpdates = c.PartialUpdates
filter.Spec = c.Spec
@ -455,14 +475,17 @@ func (pc *PropertyCollector) CreatePropertyCollector(ctx *Context, c *types.Crea
}
func (pc *PropertyCollector) DestroyPropertyCollector(ctx *Context, c *types.DestroyPropertyCollector) soap.HasFault {
pc.CancelWaitForUpdates(&types.CancelWaitForUpdates{This: c.This})
body := &methods.DestroyPropertyCollectorBody{}
for _, ref := range pc.Filter {
filter := ctx.Session.Get(ref).(*PropertyFilter)
filter.DestroyPropertyFilter(&types.DestroyPropertyFilter{This: ref})
filter.DestroyPropertyFilter(ctx, &types.DestroyPropertyFilter{This: ref})
}
ctx.Session.Remove(c.This)
ctx.Map.Remove(c.This)
body.Res = &types.DestroyPropertyCollectorResponse{}
@ -519,24 +542,46 @@ func (pc *PropertyCollector) RetrieveProperties(ctx *Context, r *types.RetrieveP
}
func (pc *PropertyCollector) CancelWaitForUpdates(r *types.CancelWaitForUpdates) soap.HasFault {
pc.mu.Lock()
if pc.cancel != nil {
pc.cancel()
}
pc.mu.Unlock()
return &methods.CancelWaitForUpdatesBody{Res: new(types.CancelWaitForUpdatesResponse)}
}
func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpdatesEx) soap.HasFault {
body := &methods.WaitForUpdatesExBody{}
func (pc *PropertyCollector) update(u types.ObjectUpdate) {
pc.mu.Lock()
pc.updates = append(pc.updates, u)
pc.mu.Unlock()
}
// 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
}
func (pc *PropertyCollector) PutObject(o mo.Reference) {
pc.update(types.ObjectUpdate{
Obj: o.Reference(),
Kind: types.ObjectUpdateKindEnter,
ChangeSet: nil,
})
}
update := &types.UpdateSet{
Version: "-",
}
func (pc *PropertyCollector) UpdateObject(o mo.Reference, changes []types.PropertyChange) {
pc.update(types.ObjectUpdate{
Obj: o.Reference(),
Kind: types.ObjectUpdateKindModify,
ChangeSet: changes,
})
}
func (pc *PropertyCollector) RemoveObject(ref types.ManagedObjectReference) {
pc.update(types.ObjectUpdate{
Obj: ref,
Kind: types.ObjectUpdateKindLeave,
ChangeSet: nil,
})
}
func (pc *PropertyCollector) apply(ctx *Context, update *types.UpdateSet) types.BaseMethodFault {
for _, ref := range pc.Filter {
filter := ctx.Session.Get(ref).(*PropertyFilter)
@ -545,8 +590,7 @@ func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpda
res, fault := pc.collect(ctx, r)
if fault != nil {
body.Fault_ = Fault("", fault)
return body
return fault
}
fu := types.PropertyFilterUpdate{
@ -554,6 +598,10 @@ func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpda
}
for _, o := range res.Objects {
if _, ok := filter.refs[o.Obj]; ok {
continue
}
filter.refs[o.Obj] = struct{}{}
ou := types.ObjectUpdate{
Obj: o.Obj,
Kind: types.ObjectUpdateKindEnter,
@ -570,14 +618,122 @@ func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpda
fu.ObjectSet = append(fu.ObjectSet, ou)
}
update.FilterSet = append(update.FilterSet, fu)
if len(fu.ObjectSet) != 0 {
update.FilterSet = append(update.FilterSet, fu)
}
}
return nil
}
func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpdatesEx) soap.HasFault {
wait, cancel := context.WithCancel(context.Background())
if r.Options != nil {
if max := r.Options.MaxWaitSeconds; max != nil {
wait, cancel = context.WithTimeout(context.Background(), time.Second*time.Duration(*max))
}
}
pc.mu.Lock()
pc.cancel = cancel
pc.mu.Unlock()
body := &methods.WaitForUpdatesExBody{}
set := &types.UpdateSet{
Version: r.Version,
}
body.Res = &types.WaitForUpdatesExResponse{
Returnval: update,
Returnval: set,
}
return body
apply := func() bool {
if fault := pc.apply(ctx, set); fault != nil {
body.Fault_ = Fault("", fault)
body.Res = nil
return false
}
return true
}
if r.Version == "" {
apply() // Collect current state
set.Version = "-" // Next request with Version set will wait via loop below
ctx.Map.AddHandler(pc) // Listen for create, update, delete of managed objects
return body
}
ticker := time.NewTicker(250 * time.Millisecond) // allow for updates to accumulate
defer ticker.Stop()
// Start the wait loop, returning on one of:
// - Client calls CancelWaitForUpdates
// - MaxWaitSeconds was specified and has been exceeded
// - We have updates to send to the client
for {
select {
case <-wait.Done():
body.Res.Returnval = nil
switch wait.Err() {
case context.Canceled:
log.Printf("%s: WaitForUpdates canceled", pc.Self)
body.Fault_ = Fault("", new(types.RequestCanceled)) // CancelWaitForUpdates was called
body.Res = nil
case context.DeadlineExceeded:
log.Printf("%s: WaitForUpdates MaxWaitSeconds exceeded", pc.Self)
}
return body
case <-ticker.C:
pc.mu.Lock()
updates := pc.updates
pc.updates = nil // clear updates collected by the managed object CRUD listeners
pc.mu.Unlock()
if len(updates) == 0 {
continue
}
log.Printf("%s: applying %d updates to %d filters", pc.Self, len(updates), len(pc.Filter))
for _, f := range pc.Filter {
filter := ctx.Session.Get(f).(*PropertyFilter)
fu := types.PropertyFilterUpdate{Filter: f}
for _, update := range updates {
switch update.Kind {
case types.ObjectUpdateKindEnter: // Create
if !apply() {
return body
}
case types.ObjectUpdateKindModify: // Update
log.Printf("%s has %d changes", update.Obj, len(update.ChangeSet))
if !apply() { // An update may apply to collector traversal specs
return body
}
if _, ok := filter.refs[update.Obj]; ok {
// This object has already been applied by the filter,
// now check if the property spec applies for this update.
update = filter.apply(ctx, update)
if len(update.ChangeSet) != 0 {
fu.ObjectSet = append(fu.ObjectSet, update)
}
}
case types.ObjectUpdateKindLeave: // Delete
if _, ok := filter.refs[update.Obj]; !ok {
continue
}
delete(filter.refs, update.Obj)
fu.ObjectSet = append(fu.ObjectSet, update)
}
}
if len(fu.ObjectSet) != 0 {
set.FilterSet = append(set.FilterSet, fu)
}
}
if len(set.FilterSet) != 0 {
return body
}
}
}
}
// WaitForUpdates is deprecated, but pyvmomi is still using it at the moment.

View File

@ -17,6 +17,9 @@ limitations under the License.
package simulator
import (
"reflect"
"strings"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
@ -26,17 +29,71 @@ import (
type PropertyFilter struct {
mo.PropertyFilter
pc *PropertyCollector
pc *PropertyCollector
refs map[types.ManagedObjectReference]struct{}
}
func (f *PropertyFilter) DestroyPropertyFilter(c *types.DestroyPropertyFilter) soap.HasFault {
func (f *PropertyFilter) DestroyPropertyFilter(ctx *Context, c *types.DestroyPropertyFilter) soap.HasFault {
body := &methods.DestroyPropertyFilterBody{}
RemoveReference(&f.pc.Filter, c.This)
Map.Remove(c.This)
ctx.Session.Remove(c.This)
body.Res = &types.DestroyPropertyFilterResponse{}
return body
}
// matches returns true if the change matches one of the filter Spec.PropSet
func (f *PropertyFilter) matches(ctx *Context, ref types.ManagedObjectReference, change *types.PropertyChange) bool {
for _, p := range f.Spec.PropSet {
if p.Type != ref.Type {
continue
}
if isTrue(p.All) {
return true
}
for _, name := range p.PathSet {
if name == change.Name {
return true
}
// strings.HasPrefix("runtime.powerState", "runtime") == parent field matches
if strings.HasPrefix(change.Name, name) {
if obj := ctx.Map.Get(ref); obj != nil { // object may have since been deleted
change.Name = name
change.Val, _ = fieldValue(reflect.ValueOf(obj), name)
}
return true
}
}
}
return false
}
// apply the PropertyFilter.Spec to the given ObjectUpdate
func (f *PropertyFilter) apply(ctx *Context, change types.ObjectUpdate) types.ObjectUpdate {
parents := make(map[string]bool)
set := change.ChangeSet
change.ChangeSet = nil
for i, p := range set {
if f.matches(ctx, change.Obj, &p) {
if p.Name != set[i].Name {
// update matches a parent field from the spec.
if parents[p.Name] {
continue // only return 1 instance of the parent
}
parents[p.Name] = true
}
change.ChangeSet = append(change.ChangeSet, p)
}
}
return change
}

View File

@ -23,6 +23,7 @@ import (
"reflect"
"strings"
"sync"
"sync/atomic"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/mo"
@ -46,10 +47,11 @@ var refValueMap = map[string]string{
// Map is the default Registry instance.
var Map = NewRegistry()
// RegisterObject interface supports callbacks when objects are added and removed from the Registry
// RegisterObject interface supports callbacks when objects are created, updated and deleted from the Registry
type RegisterObject interface {
mo.Reference
PutObject(mo.Reference)
UpdateObject(mo.Reference, []types.PropertyChange)
RemoveObject(types.ManagedObjectReference)
}
@ -59,7 +61,7 @@ type Registry struct {
objects map[types.ManagedObjectReference]mo.Reference
handlers map[types.ManagedObjectReference]RegisterObject
locks map[types.ManagedObjectReference]sync.Locker
counter int
counter int64
Namespace string
Path string
@ -112,8 +114,8 @@ func (r *Registry) newReference(item mo.Reference) types.ManagedObjectReference
}
if ref.Value == "" {
r.counter++
ref.Value = fmt.Sprintf("%s-%d", valuePrefix(ref.Type), r.counter)
n := atomic.AddInt64(&r.counter, 1)
ref.Value = fmt.Sprintf("%s-%d", valuePrefix(ref.Type), n)
}
return ref
@ -126,7 +128,9 @@ func (r *Registry) setReference(item mo.Reference, ref types.ManagedObjectRefere
// AddHandler adds a RegisterObject handler to the Registry.
func (r *Registry) AddHandler(h RegisterObject) {
r.m.Lock()
r.handlers[h.Reference()] = h
r.m.Unlock()
}
// NewEntity sets Entity().Self with a new, unique Value.
@ -173,10 +177,23 @@ func (r *Registry) Any(kind string) mo.Entity {
return nil
}
// applyHandlers calls the given func for each r.handlers
func (r *Registry) applyHandlers(f func(o RegisterObject)) {
r.m.Lock()
handlers := make([]RegisterObject, 0, len(r.handlers))
for _, handler := range r.handlers {
handlers = append(handlers, handler)
}
r.m.Unlock()
for i := range handlers {
f(handlers[i])
}
}
// 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 == "" {
@ -192,25 +209,50 @@ func (r *Registry) Put(item mo.Reference) mo.Reference {
r.objects[ref] = item
for _, h := range r.handlers {
h.PutObject(item)
}
r.m.Unlock()
r.applyHandlers(func(o RegisterObject) {
o.PutObject(item)
})
return item
}
// Remove removes an object from the Registry.
func (r *Registry) Remove(item types.ManagedObjectReference) {
r.applyHandlers(func(o RegisterObject) {
o.RemoveObject(item)
})
r.m.Lock()
defer r.m.Unlock()
for _, h := range r.handlers {
h.RemoveObject(item)
}
delete(r.objects, item)
delete(r.handlers, item)
delete(r.locks, item)
r.m.Unlock()
}
// Update dispatches object property changes to RegisterObject handlers,
// such as any PropertyCollector instances with in-progress WaitForUpdates calls.
// The changes are also applied to the given object via mo.ApplyPropertyChange,
// so there is no need to set object fields directly.
func (r *Registry) Update(obj mo.Reference, changes []types.PropertyChange) {
for i := range changes {
if changes[i].Op == "" {
changes[i].Op = types.PropertyChangeOpAssign
}
if changes[i].Val != nil {
rval := reflect.ValueOf(changes[i].Val)
changes[i].Val = wrapValue(rval, rval.Type())
}
}
val := getManagedObject(obj).Addr().Interface().(mo.Reference)
mo.ApplyPropertyChange(val, changes)
r.applyHandlers(func(o RegisterObject) {
o.UpdateObject(val, changes)
})
}
// getEntityParent traverses up the inventory and returns the first object of type kind.
@ -417,11 +459,23 @@ func (r *Registry) MarshalJSON() ([]byte, error) {
}
func (r *Registry) locker(obj mo.Reference) sync.Locker {
var ref types.ManagedObjectReference
switch x := obj.(type) {
case types.ManagedObjectReference:
ref = x
obj = r.Get(ref) // to check for sync.Locker
case *types.ManagedObjectReference:
ref = *x
obj = r.Get(ref) // to check for sync.Locker
default:
ref = obj.Reference()
}
if mu, ok := obj.(sync.Locker); ok {
return mu
}
ref := obj.Reference()
r.m.Lock()
mu, ok := r.locks[ref]
if !ok {
@ -444,3 +498,9 @@ func (r *Registry) WithLock(obj mo.Reference, f func()) {
}
f()
}
// nopLocker can be embedded to opt-out of auto-locking (see Registry.WithLock)
type nopLocker struct{}
func (*nopLocker) Lock() {}
func (*nopLocker) Unlock() {}

View File

@ -57,15 +57,18 @@ func (s *SearchIndex) FindByDatastorePath(r *types.FindByDatastorePath) soap.Has
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 {
split := func(c rune) bool {
return c == '/'
}
path := strings.FieldsFunc(req.InventoryPath, split)
if len(path) < 1 {
return body
}
root := Map.content().RootFolder
o := &root
for _, name := range path[1:] {
for _, name := range path {
f := s.FindChild(&types.FindChild{Entity: *o, Name: name})
o = f.(*methods.FindChildBody).Res.Returnval
@ -132,9 +135,16 @@ func (s *SearchIndex) FindByUuid(req *types.FindByUuid) soap.HasFault {
if !ok {
continue
}
if vm.Config.Uuid == req.Uuid {
body.Res.Returnval = &ref
break
if req.InstanceUuid != nil && *req.InstanceUuid {
if vm.Config.InstanceUuid == req.Uuid {
body.Res.Returnval = &ref
break
}
} else {
if vm.Config.Uuid == req.Uuid {
body.Res.Returnval = &ref
break
}
}
}
} else {

View File

@ -138,6 +138,12 @@ func (s *SessionManager) Logout(ctx *Context, _ *types.Logout) soap.HasFault {
session := ctx.Session
delete(s.sessions, session.Key)
for ref, obj := range ctx.Session.Registry.objects {
if _, ok := obj.(RegisterObject); ok {
ctx.Map.Remove(ref) // Remove RegisterObject handlers
}
}
ctx.postEvent(&types.UserLogoutSessionEvent{
IpAddress: session.IpAddress,
UserAgent: session.UserAgent,

View File

@ -390,7 +390,7 @@ 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"))
dc, err := finder.DatacenterOrDefault(ctx, query.Get("dcPath"))
if err != nil {
return nil, err
}

View File

@ -55,18 +55,19 @@ func CreateTask(e mo.Reference, name string, run func(*Task) (types.AnyType, typ
Execute: run,
}
Map.Put(task)
task.Self = Map.newReference(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.Reason = &types.TaskReasonUser{UserName: "vcsim"} // TODO: Context.Session.User
task.Info.QueueTime = time.Now()
task.Info.State = types.TaskInfoStateQueued
Map.Put(task)
return task
}
@ -78,25 +79,31 @@ type TaskRunner interface {
func (t *Task) Run() types.ManagedObjectReference {
now := time.Now()
t.Info.StartTime = &now
t.Info.State = types.TaskInfoStateRunning
Map.Update(t, []types.PropertyChange{
{Name: "info.startTime", Val: now},
{Name: "info.state", Val: types.TaskInfoStateRunning},
})
res, err := t.Execute(t)
now = time.Now()
t.Info.CompleteTime = &now
state := types.TaskInfoStateSuccess
var fault interface{}
if err != nil {
t.Info.State = types.TaskInfoStateError
t.Info.Error = &types.LocalizedMethodFault{
state = types.TaskInfoStateError
fault = types.LocalizedMethodFault{
Fault: err,
LocalizedMessage: fmt.Sprintf("%T", err),
}
} else {
t.Info.Result = res
t.Info.State = types.TaskInfoStateSuccess
}
now = time.Now()
Map.Update(t, []types.PropertyChange{
{Name: "info.completeTime", Val: now},
{Name: "info.state", Val: state},
{Name: "info.result", Val: res},
{Name: "info.error", Val: fault},
})
return t.Self
}

View File

@ -17,6 +17,8 @@ limitations under the License.
package simulator
import (
"sync"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
@ -26,6 +28,7 @@ var recentTaskMax = 200 // the VC limit
type TaskManager struct {
mo.TaskManager
sync.Mutex
}
func NewTaskManager(ref types.ManagedObjectReference) object.Reference {
@ -41,12 +44,16 @@ func (m *TaskManager) PutObject(obj mo.Reference) {
return
}
m.RecentTask = append(m.RecentTask, ref)
if len(m.RecentTask) > recentTaskMax {
m.RecentTask = m.RecentTask[1:]
m.Lock()
recent := append(m.RecentTask, ref)
if len(recent) > recentTaskMax {
recent = recent[1:]
}
Map.Update(m, []types.PropertyChange{{Name: "recentTask", Val: recent}})
m.Unlock()
}
func (m *TaskManager) RemoveObject(_ types.ManagedObjectReference) {
}
func (*TaskManager) RemoveObject(types.ManagedObjectReference) {}
func (*TaskManager) UpdateObject(mo.Reference, []types.PropertyChange) {}

View File

@ -137,7 +137,8 @@ type ContainerView struct {
types map[string]bool
}
func (v *ContainerView) DestroyView(c *types.DestroyView) soap.HasFault {
func (v *ContainerView) DestroyView(ctx *Context, c *types.DestroyView) soap.HasFault {
ctx.Session.Remove(c.This)
return destroyView(c.This)
}
@ -192,3 +193,83 @@ func (v *ContainerView) add(root mo.Reference, seen map[types.ManagedObjectRefer
}
})
}
func (m *ViewManager) CreateListView(ctx *Context, req *types.CreateListView) soap.HasFault {
body := new(methods.CreateListViewBody)
list := new(ListView)
if err := list.add(req.Obj); err != nil {
body.Fault_ = Fault("", err)
return body
}
ctx.Session.Put(list)
body.Res = &types.CreateListViewResponse{
Returnval: list.Self,
}
return body
}
type ListView struct {
mo.ListView
}
func (v *ListView) update() {
Map.Update(v, []types.PropertyChange{{Name: "view", Val: v.View}})
}
func (v *ListView) add(refs []types.ManagedObjectReference) *types.ManagedObjectNotFound {
for _, ref := range refs {
obj := Map.Get(ref)
if obj == nil {
return &types.ManagedObjectNotFound{Obj: ref}
}
v.View = append(v.View, ref)
}
return nil
}
func (v *ListView) DestroyView(ctx *Context, c *types.DestroyView) soap.HasFault {
ctx.Session.Remove(c.This)
return destroyView(c.This)
}
func (v *ListView) ModifyListView(req *types.ModifyListView) soap.HasFault {
body := new(methods.ModifyListViewBody)
for _, ref := range req.Remove {
RemoveReference(&v.View, ref)
}
if err := v.add(req.Add); err != nil {
body.Fault_ = Fault("", err)
return body
}
body.Res = new(types.ModifyListViewResponse)
if len(req.Remove) != 0 || len(req.Add) != 0 {
v.update()
}
return body
}
func (v *ListView) ResetListView(req *types.ResetListView) soap.HasFault {
body := new(methods.ResetListViewBody)
v.View = nil
if err := v.add(req.Obj); err != nil {
body.Fault_ = Fault("", err)
return body
}
body.Res = new(types.ResetListViewResponse)
v.update()
return body
}

View File

@ -210,3 +210,10 @@ func (m *VirtualDiskManager) QueryVirtualDiskUuid(req *types.QueryVirtualDiskUui
return body
}
func (m *VirtualDiskManager) SetVirtualDiskUuid(req *types.SetVirtualDiskUuid) soap.HasFault {
body := new(methods.SetVirtualDiskUuidBody)
// TODO: validate uuid format and persist
body.Res = new(types.SetVirtualDiskUuidResponse)
return body
}

View File

@ -444,6 +444,13 @@ func numberToString(n int64, sep rune) string {
return buf.String()
}
func getDiskSize(disk *types.VirtualDisk) int64 {
if disk.CapacityInBytes == 0 {
return disk.CapacityInKB * 1024
}
return disk.CapacityInBytes
}
func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec *types.VirtualDeviceConfigSpec) types.BaseMethodFault {
device := spec.Device
d := device.GetVirtualDevice()
@ -518,9 +525,16 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec
p, _ := parseDatastorePath(info.FileName)
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
ds := Map.FindByName(p.Datastore, host.Datastore).Reference()
info.Datastore = &ds
entity := Map.FindByName(p.Datastore, host.Datastore)
ref := entity.Reference()
info.Datastore = &ref
ds := entity.(*Datastore)
// XXX: compare disk size and free space until windows stat is supported
ds.Summary.FreeSpace -= getDiskSize(x)
ds.Info.GetDatastoreInfo().FreeSpace = ds.Summary.FreeSpace
}
}
@ -556,6 +570,15 @@ func (vm *VirtualMachine) removeDevice(devices object.VirtualDeviceList, spec *t
switch b := device.Backing.(type) {
case types.BaseVirtualDeviceFileBackingInfo:
file = b.GetVirtualDeviceFileBackingInfo().FileName
p, _ := parseDatastorePath(file)
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
ds := Map.FindByName(p.Datastore, host.Datastore).(*Datastore)
ds.Summary.FreeSpace += getDiskSize(device)
ds.Info.GetDatastoreInfo().FreeSpace = ds.Summary.FreeSpace
}
if file != "" {
@ -686,15 +709,9 @@ func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
}
}
c.VirtualMachine.Runtime.PowerState = c.state
c.VirtualMachine.Summary.Runtime.PowerState = c.state
bt := &c.VirtualMachine.Summary.Runtime.BootTime
var boot types.AnyType
if c.state == types.VirtualMachinePowerStatePoweredOn {
now := time.Now()
*bt = &now
} else {
*bt = nil
boot = time.Now()
}
event := c.event()
@ -705,9 +722,23 @@ func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
&types.VmPoweredOnEvent{VmEvent: event},
)
case types.VirtualMachinePowerStatePoweredOff:
c.ctx.postEvent(&types.VmPoweredOffEvent{VmEvent: event})
c.ctx.postEvent(
&types.VmStoppingEvent{VmEvent: event},
&types.VmPoweredOffEvent{VmEvent: event},
)
case types.VirtualMachinePowerStateSuspended:
c.ctx.postEvent(
&types.VmSuspendingEvent{VmEvent: event},
&types.VmSuspendedEvent{VmEvent: event},
)
}
Map.Update(c.VirtualMachine, []types.PropertyChange{
{Name: "runtime.powerState", Val: c.state},
{Name: "summary.runtime.powerState", Val: c.state},
{Name: "summary.runtime.bootTime", Val: boot},
})
return nil, nil
}
@ -739,6 +770,37 @@ func (vm *VirtualMachine) PowerOffVMTask(ctx *Context, c *types.PowerOffVM_Task)
}
}
func (vm *VirtualMachine) SuspendVMTask(ctx *Context, req *types.SuspendVM_Task) soap.HasFault {
runner := &powerVMTask{vm, types.VirtualMachinePowerStateSuspended, ctx}
task := CreateTask(runner.Reference(), "suspend", runner.Run)
return &methods.SuspendVM_TaskBody{
Res: &types.SuspendVM_TaskResponse{
Returnval: task.Run(),
},
}
}
func (vm *VirtualMachine) ResetVMTask(ctx *Context, req *types.ResetVM_Task) soap.HasFault {
task := CreateTask(vm, "reset", func(task *Task) (types.AnyType, types.BaseMethodFault) {
res := vm.PowerOffVMTask(ctx, &types.PowerOffVM_Task{This: vm.Self})
ctask := Map.Get(res.(*methods.PowerOffVM_TaskBody).Res.Returnval).(*Task)
if ctask.Info.Error != nil {
return nil, ctask.Info.Error.Fault
}
_ = vm.PowerOnVMTask(ctx, &types.PowerOnVM_Task{This: vm.Self})
return nil, nil
})
return &methods.ResetVM_TaskBody{
Res: &types.ResetVM_TaskResponse{
Returnval: task.Run(),
},
}
}
func (vm *VirtualMachine) ReconfigVMTask(ctx *Context, req *types.ReconfigVM_Task) soap.HasFault {
task := CreateTask(vm, "reconfigVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
err := vm.configure(&req.Spec)
@ -771,6 +833,11 @@ func (vm *VirtualMachine) DestroyTask(ctx *Context, req *types.Destroy_Task) soa
return nil, r.Fault().VimFault().(types.BaseMethodFault)
}
// Remove all devices
devices := object.VirtualDeviceList(vm.Config.Hardware.Device)
spec, _ := devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationRemove)
vm.configureDevices(&types.VirtualMachineConfigSpec{DeviceChange: spec})
// Delete VM files from the datastore (ignoring result for now)
m := Map.FileManager()
dc := Map.getEntityDatacenter(vm).Reference()
@ -907,29 +974,36 @@ func (vm *VirtualMachine) CloneVMTask(ctx *Context, req *types.CloneVM_Task) soa
func (vm *VirtualMachine) RelocateVMTask(req *types.RelocateVM_Task) soap.HasFault {
task := CreateTask(vm, "relocateVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
var changes []types.PropertyChange
if ref := req.Spec.Datastore; ref != nil {
ds := Map.Get(*ref).(*Datastore)
Map.RemoveReference(ds, &ds.Vm, *ref)
vm.Datastore = []types.ManagedObjectReference{*ref}
// TODO: migrate vm.Config.Files (and vm.Summary.Config.VmPathName)
changes = append(changes, types.PropertyChange{Name: "datastore", Val: []types.ManagedObjectReference{*ref}})
}
if ref := req.Spec.Pool; ref != nil {
pool := Map.Get(*ref).(*ResourcePool)
Map.RemoveReference(pool, &pool.Vm, *ref)
vm.ResourcePool = ref
changes = append(changes, types.PropertyChange{Name: "resourcePool", Val: *ref})
}
if ref := req.Spec.Host; ref != nil {
host := Map.Get(*ref).(*HostSystem)
Map.RemoveReference(host, &host.Vm, *ref)
vm.Runtime.Host = ref
changes = append(changes,
types.PropertyChange{Name: "runtime.host", Val: *ref},
types.PropertyChange{Name: "summary.runtime.host", Val: *ref},
)
}
Map.Update(vm, changes)
return nil, nil
})
@ -1032,7 +1106,7 @@ func (vm *VirtualMachine) RemoveAllSnapshotsTask(req *types.RemoveAllSnapshots_T
}
}
func (vm *VirtualMachine) ShutdownGuest(c *types.ShutdownGuest) soap.HasFault {
func (vm *VirtualMachine) ShutdownGuest(ctx *Context, c *types.ShutdownGuest) soap.HasFault {
r := &methods.ShutdownGuestBody{}
// should be poweron
if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOff {
@ -1047,6 +1121,17 @@ func (vm *VirtualMachine) ShutdownGuest(c *types.ShutdownGuest) soap.HasFault {
vm.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff
vm.Summary.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff
event := vm.event()
ctx.postEvent(
&types.VmGuestShutdownEvent{VmEvent: event},
&types.VmPoweredOffEvent{VmEvent: event},
)
Map.Update(vm, []types.PropertyChange{
{Name: "runtime.powerState", Val: types.VirtualMachinePowerStatePoweredOff},
{Name: "summary.runtime.powerState", Val: types.VirtualMachinePowerStatePoweredOff},
})
r.Res = new(types.ShutdownGuestResponse)
return r

32
vendor/github.com/vmware/govmomi/vapi/tags/BUILD generated vendored Normal file
View File

@ -0,0 +1,32 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"categories.go",
"rest_client.go",
"tag_association.go",
"tags.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/vmware/govmomi/vapi/tags",
importpath = "github.com/vmware/govmomi/vapi/tags",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/vmware/govmomi/vim25/soap:go_default_library",
"//vendor/github.com/vmware/govmomi/vim25/types:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,226 @@
// 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 tags
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
)
const (
CategoryURL = "/com/vmware/cis/tagging/category"
ErrAlreadyExists = "already_exists"
)
type CategoryCreateSpec struct {
CreateSpec CategoryCreate `json:"create_spec"`
}
type CategoryUpdateSpec struct {
UpdateSpec CategoryUpdate `json:"update_spec,omitempty"`
}
type CategoryCreate struct {
AssociableTypes []string `json:"associable_types"`
Cardinality string `json:"cardinality"`
Description string `json:"description"`
Name string `json:"name"`
}
type CategoryUpdate struct {
AssociableTypes []string `json:"associable_types,omitempty"`
Cardinality string `json:"cardinality,omitempty"`
Description string `json:"description,omitempty"`
Name string `json:"name,omitempty"`
}
type Category struct {
ID string `json:"id"`
Description string `json:"description"`
Name string `json:"name"`
Cardinality string `json:"cardinality"`
AssociableTypes []string `json:"associable_types"`
UsedBy []string `json:"used_by"`
}
type CategoryInfo struct {
Name string
CategoryID string
}
func (c *RestClient) CreateCategoryIfNotExist(ctx context.Context, name string, description string, categoryType string, multiValue bool) (*string, error) {
categories, err := c.GetCategoriesByName(ctx, name)
if err != nil {
return nil, err
}
if categories == nil {
var multiValueStr string
if multiValue {
multiValueStr = "MULTIPLE"
} else {
multiValueStr = "SINGLE"
}
categoryCreate := CategoryCreate{[]string{categoryType}, multiValueStr, description, name}
spec := CategoryCreateSpec{categoryCreate}
id, err := c.CreateCategory(ctx, &spec)
if err != nil {
// in case there are two docker daemon try to create inventory category, query the category once again
if strings.Contains(err.Error(), "ErrAlreadyExists") {
if categories, err = c.GetCategoriesByName(ctx, name); err != nil {
return nil, fmt.Errorf("failed to get inventory category for %s", err)
}
} else {
return nil, fmt.Errorf("failed to create inventory category for %s", err)
}
} else {
return id, nil
}
}
if categories != nil {
return &categories[0].ID, nil
}
// should not happen
return nil, fmt.Errorf("failed to create inventory for it's existed, but could not query back. Please check system")
}
func (c *RestClient) CreateCategory(ctx context.Context, spec *CategoryCreateSpec) (*string, error) {
stream, _, status, err := c.call(ctx, http.MethodPost, CategoryURL, spec, nil)
if status != http.StatusOK || err != nil {
return nil, fmt.Errorf("create category failed with status code: %d, error message: %s", status, err)
}
type RespValue struct {
Value string
}
var pID RespValue
if err := json.NewDecoder(stream).Decode(&pID); err != nil {
return nil, fmt.Errorf("decode response body failed for: %s", err)
}
return &(pID.Value), nil
}
func (c *RestClient) GetCategory(ctx context.Context, id string) (*Category, error) {
stream, _, status, err := c.call(ctx, http.MethodGet, fmt.Sprintf("%s/id:%s", CategoryURL, id), nil, nil)
if status != http.StatusOK || err != nil {
return nil, fmt.Errorf("get category failed with status code: %d, error message: %s", status, err)
}
type RespValue struct {
Value Category
}
var pCategory RespValue
if err := json.NewDecoder(stream).Decode(&pCategory); err != nil {
return nil, fmt.Errorf("decode response body failed for: %s", err)
}
return &(pCategory.Value), nil
}
func (c *RestClient) UpdateCategory(ctx context.Context, id string, spec *CategoryUpdateSpec) error {
_, _, status, err := c.call(ctx, http.MethodPatch, fmt.Sprintf("%s/id:%s", CategoryURL, id), spec, nil)
if status != http.StatusOK || err != nil {
return fmt.Errorf("update category failed with status code: %d, error message: %s", status, err)
}
return nil
}
func (c *RestClient) DeleteCategory(ctx context.Context, id string) error {
_, _, status, err := c.call(ctx, http.MethodDelete, fmt.Sprintf("%s/id:%s", CategoryURL, id), nil, nil)
if status != http.StatusOK || err != nil {
return fmt.Errorf("delete category failed with status code: %d, error message: %s", status, err)
}
return nil
}
func (c *RestClient) ListCategories(ctx context.Context) ([]string, error) {
stream, _, status, err := c.call(ctx, http.MethodGet, CategoryURL, nil, nil)
if status != http.StatusOK || err != nil {
return nil, fmt.Errorf("get categories failed with status code: %d, error message: %s", status, err)
}
type Categories struct {
Value []string
}
var pCategories Categories
if err := json.NewDecoder(stream).Decode(&pCategories); err != nil {
return nil, fmt.Errorf("decode response body failed for: %s", err)
}
return pCategories.Value, nil
}
func (c *RestClient) ListCategoriesByName(ctx context.Context) ([]CategoryInfo, error) {
categoryIds, err := c.ListCategories(ctx)
if err != nil {
return nil, fmt.Errorf("get category failed for: %s", err)
}
var categoryInfoSlice []CategoryInfo
for _, cID := range categoryIds {
category, err := c.GetCategory(ctx, cID)
if err != nil {
return nil, fmt.Errorf("get category %s failed for %s", cID, err)
}
categoryCreate := &CategoryInfo{Name: category.Name, CategoryID: category.ID}
categoryInfoSlice = append(categoryInfoSlice, *categoryCreate)
}
return categoryInfoSlice, nil
}
func (c *RestClient) GetCategoriesByName(ctx context.Context, name string) ([]Category, error) {
categoryIds, err := c.ListCategories(ctx)
if err != nil {
return nil, fmt.Errorf("get category failed for: %s", err)
}
var categories []Category
for _, cID := range categoryIds {
category, err := c.GetCategory(ctx, cID)
if err != nil {
return nil, fmt.Errorf("get category %s failed for %s", cID, err)
}
if category.Name == name {
categories = append(categories, *category)
}
}
return categories, nil
}

View File

@ -0,0 +1,272 @@
// 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 tags
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"sync"
"github.com/vmware/govmomi/vim25/soap"
)
const (
RestPrefix = "/rest"
loginURL = "/com/vmware/cis/session"
sessionIDCookieName = "vmware-api-session-id"
)
type RestClient struct {
mu sync.Mutex
host string
scheme string
endpoint *url.URL
user *url.Userinfo
HTTP *http.Client
cookies []*http.Cookie
}
func NewClient(u *url.URL, insecure bool, thumbprint string) *RestClient {
endpoint := &url.URL{}
*endpoint = *u
endpoint.Path = RestPrefix
// Ignore "#" anchor
endpoint.Fragment = ""
sc := soap.NewClient(endpoint, insecure)
if thumbprint != "" {
sc.SetThumbprint(endpoint.Host, thumbprint)
}
user := endpoint.User
endpoint.User = nil
return &RestClient{
endpoint: endpoint,
user: user,
host: endpoint.Host,
scheme: endpoint.Scheme,
HTTP: &sc.Client,
}
}
// NewClientWithSessionID creates a new REST client with a supplied session ID
// to re-connect to existing sessions.
//
// Note that the session is not checked for validity - to check for a valid
// session after creating the client, use the Valid method. If the session is
// no longer valid and the session needs to be re-saved, Login should be called
// again before calling SessionID to extract the new session ID. Clients
// created with this function function work in the exact same way as clients
// created with NewClient, including supporting re-login on invalid sessions on
// all SDK calls.
func NewClientWithSessionID(u *url.URL, insecure bool, thumbprint string, sessionID string) *RestClient {
c := NewClient(u, insecure, thumbprint)
c.SetSessionID(sessionID)
return c
}
func (c *RestClient) encodeData(data interface{}) (*bytes.Buffer, error) {
params := bytes.NewBuffer(nil)
if data != nil {
if err := json.NewEncoder(params).Encode(data); err != nil {
return nil, err
}
}
return params, nil
}
func (c *RestClient) call(ctx context.Context, method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error) {
// Logger.Debugf("%s: %s, headers: %+v", method, path, headers)
params, err := c.encodeData(data)
if err != nil {
return nil, nil, -1, err
}
if data != nil {
if headers == nil {
headers = make(map[string][]string)
}
headers["Content-Type"] = []string{"application/json"}
}
body, hdr, statusCode, err := c.clientRequest(ctx, method, path, params, headers)
if statusCode == http.StatusUnauthorized && strings.Contains(err.Error(), "This method requires authentication") {
c.Login(ctx)
return c.clientRequest(ctx, method, path, params, headers)
}
return body, hdr, statusCode, err
}
func (c *RestClient) clientRequest(ctx context.Context, method, path string, in io.Reader, headers map[string][]string) (io.ReadCloser, http.Header, int, error) {
expectedPayload := (method == http.MethodPost || method == http.MethodPut)
if expectedPayload && in == nil {
in = bytes.NewReader([]byte{})
}
req, err := c.newRequest(method, path, in)
if err != nil {
return nil, nil, -1, err
}
req = req.WithContext(ctx)
c.mu.Lock()
if c.cookies != nil {
req.AddCookie(c.cookies[0])
}
c.mu.Unlock()
if headers != nil {
for k, v := range headers {
req.Header[k] = v
}
}
if expectedPayload && req.Header.Get("Content-Type") == "" {
req.Header.Set("Content-Type", "application/json")
}
req.Header.Set("Accept", "application/json")
resp, err := c.HTTP.Do(req)
return c.handleResponse(resp, err)
}
func (c *RestClient) handleResponse(resp *http.Response, err error) (io.ReadCloser, http.Header, int, error) {
statusCode := -1
if resp != nil {
statusCode = resp.StatusCode
}
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
return nil, nil, statusCode, err
}
return nil, nil, statusCode, err
}
if statusCode < http.StatusOK || statusCode >= http.StatusBadRequest {
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, nil, statusCode, err
}
if len(body) == 0 {
return nil, nil, statusCode, err
}
return nil, nil, statusCode, fmt.Errorf("error response: %s", bytes.TrimSpace(body))
}
return resp.Body, resp.Header, statusCode, nil
}
func (c *RestClient) Login(ctx context.Context) error {
request, err := c.newRequest(http.MethodPost, loginURL, nil)
if err != nil {
return err
}
if c.user != nil {
password, _ := c.user.Password()
request.SetBasicAuth(c.user.Username(), password)
}
resp, err := c.HTTP.Do(request)
if err != nil {
return err
}
if resp == nil {
return err
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return err
}
c.cookies = resp.Cookies()
return nil
}
func (c *RestClient) Logout(ctx context.Context) error {
_, _, status, err := c.call(ctx, http.MethodDelete, loginURL, nil, nil)
if status != http.StatusOK || err != nil {
return err
}
c.SetSessionID("")
return nil
}
func (c *RestClient) newRequest(method, urlStr string, body io.Reader) (*http.Request, error) {
return http.NewRequest(method, c.endpoint.String()+urlStr, body)
}
// SessionID returns the current session ID of the REST client. An empty string
// means there was no session cookie currently loaded.
func (c *RestClient) SessionID() string {
for _, cookie := range c.cookies {
if cookie.Name == sessionIDCookieName {
return cookie.Value
}
}
return ""
}
// SetSessionID sets the session cookie with the supplied session ID.
//
// This does not necessarily mean the session is valid. The session should be
// checked with Valid before proceeding, and logged back in if it has expired.
//
// This function will overwrite any existing session.
func (c *RestClient) SetSessionID(sessionID string) {
idx := -1
for i, cookie := range c.cookies {
if cookie.Name == sessionIDCookieName {
idx = i
}
}
sessionCookie := &http.Cookie{
Name: sessionIDCookieName,
Value: sessionID,
Path: RestPrefix,
}
if idx > -1 {
c.cookies[idx] = sessionCookie
} else {
c.cookies = append(c.cookies, sessionCookie)
}
}
// Valid checks to see if the session cookies in a REST client are still valid.
// This should be used when restoring a session to determine if a new login is
// necessary.
func (c *RestClient) Valid(ctx context.Context) bool {
_, _, statusCode, err := c.clientRequest(ctx, http.MethodPost, loginURL+"?~action=get", nil, nil)
if err != nil {
return false
}
if statusCode == http.StatusOK {
return true
}
return false
}

139
vendor/github.com/vmware/govmomi/vapi/tags/tag_association.go generated vendored Executable file
View File

@ -0,0 +1,139 @@
// 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 tags
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/vmware/govmomi/vim25/types"
)
const (
TagAssociationURL = "/com/vmware/cis/tagging/tag-association"
)
type AssociatedObject struct {
ID string `json:"id"`
Type string `json:"type"`
}
type TagAssociationSpec struct {
ObjectID *AssociatedObject `json:"object_id,omitempty"`
TagID *string `json:"tag_id,omitempty"`
}
type AttachedTagsInfo struct {
Name string
TagID string
}
func (c *RestClient) getAssociatedObject(ref *types.ManagedObjectReference) *AssociatedObject {
if ref == nil {
return nil
}
object := AssociatedObject{
ID: ref.Value,
Type: ref.Type,
}
return &object
}
func (c *RestClient) getAssociationSpec(tagID *string, ref *types.ManagedObjectReference) *TagAssociationSpec {
object := c.getAssociatedObject(ref)
spec := TagAssociationSpec{
TagID: tagID,
ObjectID: object,
}
return &spec
}
func (c *RestClient) AttachTagToObject(ctx context.Context, tagID string, ref *types.ManagedObjectReference) error {
spec := c.getAssociationSpec(&tagID, ref)
_, _, status, err := c.call(ctx, http.MethodPost, fmt.Sprintf("%s?~action=attach", TagAssociationURL), *spec, nil)
if status != http.StatusOK || err != nil {
return fmt.Errorf("attach tag failed with status code: %d, error message: %s", status, err)
}
return nil
}
func (c *RestClient) DetachTagFromObject(ctx context.Context, tagID string, ref *types.ManagedObjectReference) error {
spec := c.getAssociationSpec(&tagID, ref)
_, _, status, err := c.call(ctx, http.MethodPost, fmt.Sprintf("%s?~action=detach", TagAssociationURL), *spec, nil)
if status != http.StatusOK || err != nil {
return fmt.Errorf("detach tag failed with status code: %d, error message: %s", status, err)
}
return nil
}
func (c *RestClient) ListAttachedTags(ctx context.Context, ref *types.ManagedObjectReference) ([]string, error) {
spec := c.getAssociationSpec(nil, ref)
stream, _, status, err := c.call(ctx, http.MethodPost, fmt.Sprintf("%s?~action=list-attached-tags", TagAssociationURL), *spec, nil)
if status != http.StatusOK || err != nil {
return nil, fmt.Errorf("detach tag failed with status code: %d, error message: %s", status, err)
}
type RespValue struct {
Value []string
}
var pTag RespValue
if err := json.NewDecoder(stream).Decode(&pTag); err != nil {
return nil, fmt.Errorf("decode response body failed for: %s", err)
}
return pTag.Value, nil
}
func (c *RestClient) ListAttachedTagsByName(ctx context.Context, ref *types.ManagedObjectReference) ([]AttachedTagsInfo, error) {
tagIds, err := c.ListAttachedTags(ctx, ref)
if err != nil {
return nil, fmt.Errorf("get attached tag failed for: %s", err)
}
var attachedTagsInfoSlice []AttachedTagsInfo
for _, cID := range tagIds {
tag, err := c.GetTag(ctx, cID)
if err != nil {
return nil, fmt.Errorf("get tag %s failed for %s", cID, err)
}
attachedTagsCreate := &AttachedTagsInfo{Name: tag.Name, TagID: tag.ID}
attachedTagsInfoSlice = append(attachedTagsInfoSlice, *attachedTagsCreate)
}
return attachedTagsInfoSlice, nil
}
func (c *RestClient) ListAttachedObjects(ctx context.Context, tagID string) ([]AssociatedObject, error) {
spec := c.getAssociationSpec(&tagID, nil)
stream, _, status, err := c.call(ctx, http.MethodPost, fmt.Sprintf("%s?~action=list-attached-objects", TagAssociationURL), *spec, nil)
if status != http.StatusOK || err != nil {
return nil, fmt.Errorf("list object failed with status code: %d, error message: %s", status, err)
}
type RespValue struct {
Value []AssociatedObject
}
var pTag RespValue
if err := json.NewDecoder(stream).Decode(&pTag); err != nil {
return nil, fmt.Errorf("decode response body failed for: %s", err)
}
return pTag.Value, nil
}

252
vendor/github.com/vmware/govmomi/vapi/tags/tags.go generated vendored Normal file
View File

@ -0,0 +1,252 @@
// 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 tags
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
const (
TagURL = "/com/vmware/cis/tagging/tag"
)
type TagCreateSpec struct {
CreateSpec TagCreate `json:"create_spec"`
}
type TagCreate struct {
CategoryID string `json:"category_id"`
Description string `json:"description"`
Name string `json:"name"`
}
type TagUpdateSpec struct {
UpdateSpec TagUpdate `json:"update_spec,omitempty"`
}
type TagUpdate struct {
Description string `json:"description,omitempty"`
Name string `json:"name,omitempty"`
}
type Tag struct {
ID string `json:"id"`
Description string `json:"description"`
Name string `json:"name"`
CategoryID string `json:"category_id"`
UsedBy []string `json:"used_by"`
}
func (c *RestClient) CreateTagIfNotExist(ctx context.Context, name string, description string, categoryID string) (*string, error) {
tagCreate := TagCreate{categoryID, description, name}
spec := TagCreateSpec{tagCreate}
id, err := c.CreateTag(ctx, &spec)
if err == nil {
return id, nil
}
// if already exists, query back
if strings.Contains(err.Error(), ErrAlreadyExists) {
tagObjs, err := c.GetTagByNameForCategory(ctx, name, categoryID)
if err != nil {
return nil, err
}
if tagObjs != nil {
return &tagObjs[0].ID, nil
}
// should not happen
return nil, fmt.Errorf("failed to create tag for it's existed, but could not query back. Please check system")
}
return nil, fmt.Errorf("created tag failed for %s", err)
}
func (c *RestClient) DeleteTagIfNoObjectAttached(ctx context.Context, id string) error {
objs, err := c.ListAttachedObjects(ctx, id)
if err != nil {
return err
}
if len(objs) > 0 {
return fmt.Errorf("tag %s related objects is not empty, do not delete it", id)
}
return c.DeleteTag(ctx, id)
}
func (c *RestClient) CreateTag(ctx context.Context, spec *TagCreateSpec) (*string, error) {
stream, _, status, err := c.call(ctx, http.MethodPost, TagURL, spec, nil)
if status != http.StatusOK || err != nil {
return nil, fmt.Errorf("create tag failed with status code: %d, error message: %s", status, err)
}
type RespValue struct {
Value string
}
var pID RespValue
if err := json.NewDecoder(stream).Decode(&pID); err != nil {
return nil, fmt.Errorf("decode response body failed for: %s", err)
}
return &pID.Value, nil
}
func (c *RestClient) GetTag(ctx context.Context, id string) (*Tag, error) {
stream, _, status, err := c.call(ctx, http.MethodGet, fmt.Sprintf("%s/id:%s", TagURL, id), nil, nil)
if status != http.StatusOK || err != nil {
return nil, fmt.Errorf("get tag failed with status code: %d, error message: %s", status, err)
}
type RespValue struct {
Value Tag
}
var pTag RespValue
if err := json.NewDecoder(stream).Decode(&pTag); err != nil {
return nil, fmt.Errorf("decode response body failed for: %s", err)
}
return &(pTag.Value), nil
}
func (c *RestClient) UpdateTag(ctx context.Context, id string, spec *TagUpdateSpec) error {
_, _, status, err := c.call(ctx, http.MethodPatch, fmt.Sprintf("%s/id:%s", TagURL, id), spec, nil)
if status != http.StatusOK || err != nil {
return fmt.Errorf("update tag failed with status code: %d, error message: %s", status, err)
}
return nil
}
func (c *RestClient) DeleteTag(ctx context.Context, id string) error {
_, _, status, err := c.call(ctx, http.MethodDelete, fmt.Sprintf("%s/id:%s", TagURL, id), nil, nil)
if status != http.StatusOK || err != nil {
return fmt.Errorf("delete tag failed with status code: %d, error message: %s", status, err)
}
return nil
}
func (c *RestClient) ListTags(ctx context.Context) ([]string, error) {
stream, _, status, err := c.call(ctx, http.MethodGet, TagURL, nil, nil)
if status != http.StatusOK || err != nil {
return nil, fmt.Errorf("get tags failed with status code: %d, error message: %s", status, err)
}
return c.handleTagIDList(stream)
}
type TagsInfo struct {
Name string
TagID string
}
func (c *RestClient) ListTagsByName(ctx context.Context) ([]TagsInfo, error) {
tagIds, err := c.ListTags(ctx)
if err != nil {
return nil, fmt.Errorf("get tags failed for: %s", err)
}
var tagsInfoSlice []TagsInfo
for _, cID := range tagIds {
tag, err := c.GetTag(ctx, cID)
if err != nil {
return nil, fmt.Errorf("get category %s failed for %s", cID, err)
}
tagsCreate := &TagsInfo{Name: tag.Name, TagID: tag.ID}
tagsInfoSlice = append(tagsInfoSlice, *tagsCreate)
}
return tagsInfoSlice, nil
}
func (c *RestClient) ListTagsForCategory(ctx context.Context, id string) ([]string, error) {
type PostCategory struct {
ID string `json:"category_id"`
}
spec := PostCategory{id}
stream, _, status, err := c.call(ctx, http.MethodPost, fmt.Sprintf("%s/id:%s?~action=list-tags-for-category", TagURL, id), spec, nil)
if status != http.StatusOK || err != nil {
return nil, fmt.Errorf("list tags for category failed with status code: %d, error message: %s", status, err)
}
return c.handleTagIDList(stream)
}
func (c *RestClient) ListTagsInfoForCategory(ctx context.Context, id string) ([]TagsInfo, error) {
type PostCategory struct {
ID string `json:"category_id"`
}
spec := PostCategory{id}
stream, _, status, err := c.call(ctx, http.MethodPost, fmt.Sprintf("%s/id:%s?~action=list-tags-for-category", TagURL, id), spec, nil)
if status != http.StatusOK || err != nil {
return nil, fmt.Errorf("list tags for category failed with status code: %d, error message: %s", status, err)
}
var tagsInfoSlice []TagsInfo
tmp, err := c.handleTagIDList(stream)
for _, item := range tmp {
tag, err := c.GetTag(ctx, item)
if err != nil {
return nil, fmt.Errorf("get category %s failed for %s", item, err)
}
tagsCreate := &TagsInfo{Name: tag.Name, TagID: tag.ID}
tagsInfoSlice = append(tagsInfoSlice, *tagsCreate)
}
return tagsInfoSlice, nil
}
func (c *RestClient) handleTagIDList(stream io.ReadCloser) ([]string, error) {
type Tags struct {
Value []string
}
var pTags Tags
if err := json.NewDecoder(stream).Decode(&pTags); err != nil {
return nil, fmt.Errorf("decode response body failed for: %s", err)
}
return pTags.Value, nil
}
// Get tag through tag name and category id
func (c *RestClient) GetTagByNameForCategory(ctx context.Context, name string, id string) ([]Tag, error) {
tagIds, err := c.ListTagsForCategory(ctx, id)
if err != nil {
return nil, fmt.Errorf("get tag failed for %s", err)
}
var tags []Tag
for _, tID := range tagIds {
tag, err := c.GetTag(ctx, tID)
if err != nil {
return nil, fmt.Errorf("get tag %s failed for %s", tID, err)
}
if tag.Name == name {
tags = append(tags, *tag)
}
}
return tags, nil
}

View File

@ -28,7 +28,7 @@ import (
const (
Namespace = "vim25"
Version = "6.5"
Version = "6.7"
Path = "/sdk"
)

View File

@ -65,6 +65,22 @@ func ObjectContentToType(o types.ObjectContent) (interface{}, error) {
return v.Elem().Interface(), nil
}
// ApplyPropertyChange converts the response of a call to WaitForUpdates
// and applies it to the given managed object.
func ApplyPropertyChange(obj Reference, changes []types.PropertyChange) {
t := typeInfoForType(obj.Reference().Type)
v := reflect.ValueOf(obj)
for _, p := range changes {
rv, ok := t.props[p.Name]
if !ok {
continue
}
assignValue(v, rv, reflect.ValueOf(p.Val))
}
}
// LoadRetrievePropertiesResponse converts the response of a call to
// RetrieveProperties to one or more managed objects.
func LoadRetrievePropertiesResponse(res *types.RetrievePropertiesResponse, dst interface{}) error {

View File

@ -155,6 +155,8 @@ func (t *typeInfo) build(typ reflect.Type, fn string, fi []int) {
}
}
var nilValue reflect.Value
// assignValue assignes a value 'pv' to the struct pointed to by 'val', given a
// slice of field indices. It recurses into the struct until it finds the field
// specified by the indices. It creates new values for pointer types where
@ -172,6 +174,11 @@ func assignValue(val reflect.Value, fi []int, pv reflect.Value) {
rv := val.Field(fi[0])
fi = fi[1:]
if len(fi) == 0 {
if pv == nilValue {
pv = reflect.Zero(rv.Type())
rv.Set(pv)
return
}
rt := rv.Type()
pt := pv.Type()
@ -182,6 +189,24 @@ func assignValue(val reflect.Value, fi []int, pv reflect.Value) {
rt = rv.Type()
}
// If the target type is a slice, but the source is not, deference any ArrayOfXYZ type
if rt.Kind() == reflect.Slice && pt.Kind() != reflect.Slice {
if pt.Kind() == reflect.Ptr {
pv = pv.Elem()
pt = pt.Elem()
}
m := arrayOfRegexp.FindStringSubmatch(pt.Name())
if len(m) > 0 {
pv = pv.FieldByName(m[1]) // ArrayOfXYZ type has single field named XYZ
pt = pv.Type()
if !pv.IsValid() {
panic(fmt.Sprintf("expected %s type to have field %s", m[0], m[1]))
}
}
}
// If type is an interface, check if pv implements it.
if rt.Kind() == reflect.Interface && !pt.Implements(rt) {
// Check if pointer to pv implements it.
@ -200,7 +225,7 @@ func assignValue(val reflect.Value, fi []int, pv reflect.Value) {
} else if rt.ConvertibleTo(pt) {
rv.Set(pv.Convert(rt))
} else {
panic(fmt.Sprintf("cannot assign %s (%s) to %s (%s)", rt.Name(), rt.Kind(), pt.Name(), pt.Kind()))
panic(fmt.Sprintf("cannot assign %q (%s) to %q (%s)", rt.Name(), rt.Kind(), pt.Name(), pt.Kind()))
}
return
@ -211,23 +236,6 @@ func assignValue(val reflect.Value, fi []int, pv reflect.Value) {
var arrayOfRegexp = regexp.MustCompile("ArrayOf(.*)$")
func anyTypeToValue(t interface{}) reflect.Value {
rt := reflect.TypeOf(t)
rv := reflect.ValueOf(t)
// Dereference if ArrayOfXYZ type
m := arrayOfRegexp.FindStringSubmatch(rt.Name())
if len(m) > 0 {
// ArrayOfXYZ type has single field named XYZ
rv = rv.FieldByName(m[1])
if !rv.IsValid() {
panic(fmt.Sprintf("expected %s type to have field %s", m[0], m[1]))
}
}
return rv
}
// LoadObjectFromContent loads properties from the 'PropSet' field in the
// specified ObjectContent value into the value it represents, which is
// returned as a reflect.Value.
@ -240,7 +248,7 @@ func (t *typeInfo) LoadFromObjectContent(o types.ObjectContent) (reflect.Value,
if !ok {
continue
}
assignValue(v, rv, anyTypeToValue(p.Val))
assignValue(v, rv, reflect.ValueOf(p.Val))
}
return v, nil

View File

@ -77,6 +77,17 @@ type Client struct {
var schemeMatch = regexp.MustCompile(`^\w+://`)
type errInvalidCACertificate struct {
File string
}
func (e errInvalidCACertificate) Error() string {
return fmt.Sprintf(
"invalid certificate '%s', cannot be used as a trusted CA certificate",
e.File,
)
}
// ParseURL is wrapper around url.Parse, where Scheme defaults to "https" and Path defaults to "/sdk"
func ParseURL(s string) (*url.URL, error) {
var err error
@ -200,7 +211,11 @@ func (c *Client) SetRootCAs(file string) error {
return err
}
pool.AppendCertsFromPEM(pem)
if ok := pool.AppendCertsFromPEM(pem); !ok {
return errInvalidCACertificate{
File: name,
}
}
}
c.t.TLSClientConfig.RootCAs = pool

View File

@ -39,7 +39,11 @@ func (s soapFaultError) Error() string {
msg := s.fault.String
if msg == "" {
msg = reflect.TypeOf(s.fault.Detail.Fault).Name()
if s.fault.Detail.Fault == nil {
msg = "unknown fault"
} else {
msg = reflect.TypeOf(s.fault.Detail.Fault).Name()
}
}
return fmt.Sprintf("%s: %s", s.fault.Code, msg)