diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index fc9a112d676..7729c2a3431 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -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", diff --git a/Godeps/LICENSES b/Godeps/LICENSES index 85f446ee62a..63016df9aa5 100644 --- a/Godeps/LICENSES +++ b/Godeps/LICENSES @@ -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: = diff --git a/pkg/cloudprovider/providers/vsphere/BUILD b/pkg/cloudprovider/providers/vsphere/BUILD index 8c0be034f9c..3eaca85d629 100644 --- a/pkg/cloudprovider/providers/vsphere/BUILD +++ b/pkg/cloudprovider/providers/vsphere/BUILD @@ -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", diff --git a/pkg/cloudprovider/providers/vsphere/vclib/datacenter.go b/pkg/cloudprovider/providers/vsphere/vclib/datacenter.go index 96107a4a3d7..9a4eddd074d 100644 --- a/pkg/cloudprovider/providers/vsphere/vclib/datacenter.go +++ b/pkg/cloudprovider/providers/vsphere/vclib/datacenter.go @@ -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) { diff --git a/pkg/cloudprovider/providers/vsphere/vsphere.go b/pkg/cloudprovider/providers/vsphere/vsphere.go index 0ede2bd5215..6222cb0559f 100644 --- a/pkg/cloudprovider/providers/vsphere/vsphere.go +++ b/pkg/cloudprovider/providers/vsphere/vsphere.go @@ -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 +} diff --git a/pkg/cloudprovider/providers/vsphere/vsphere_test.go b/pkg/cloudprovider/providers/vsphere/vsphere_test.go index 4220a327485..1b0becca6e3 100644 --- a/pkg/cloudprovider/providers/vsphere/vsphere_test.go +++ b/pkg/cloudprovider/providers/vsphere/vsphere_test.go @@ -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") } } diff --git a/vendor/github.com/vmware/govmomi/.mailmap b/vendor/github.com/vmware/govmomi/.mailmap index c0e235c5783..a012bc9ff41 100644 --- a/vendor/github.com/vmware/govmomi/.mailmap +++ b/vendor/github.com/vmware/govmomi/.mailmap @@ -7,6 +7,7 @@ Cédric Blomart cedric David Stark Eric Gray Eric Yutao eric +Fabio Rapposelli Henrik Hodne Jeremy Canady Pieter Noordhuis diff --git a/vendor/github.com/vmware/govmomi/.travis.yml b/vendor/github.com/vmware/govmomi/.travis.yml index 0a3834fffa6..5c42befd337 100644 --- a/vendor/github.com/vmware/govmomi/.travis.yml +++ b/vendor/github.com/vmware/govmomi/.travis.yml @@ -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' \ No newline at end of file + go: '1.10' diff --git a/vendor/github.com/vmware/govmomi/BUILD b/vendor/github.com/vmware/govmomi/BUILD index 24fdb77cb51..8a4e487f0a7 100644 --- a/vendor/github.com/vmware/govmomi/BUILD +++ b/vendor/github.com/vmware/govmomi/BUILD @@ -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"], diff --git a/vendor/github.com/vmware/govmomi/CHANGELOG.md b/vendor/github.com/vmware/govmomi/CHANGELOG.md index 7cdf825a11e..c8afb958f43 100644 --- a/vendor/github.com/vmware/govmomi/CHANGELOG.md +++ b/vendor/github.com/vmware/govmomi/CHANGELOG.md @@ -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 diff --git a/vendor/github.com/vmware/govmomi/CONTRIBUTORS b/vendor/github.com/vmware/govmomi/CONTRIBUTORS index 11cf9de09f9..1fd37b609d1 100644 --- a/vendor/github.com/vmware/govmomi/CONTRIBUTORS +++ b/vendor/github.com/vmware/govmomi/CONTRIBUTORS @@ -6,9 +6,11 @@ Abhijeet Kasurde abrarshivani Adam Shannon +akutz Alessandro Cortiana Alex Bozhenko Alvaro Miranda +amandahla Amanda H. L. de Andrade Amit Bathla amit bezalel @@ -23,14 +25,15 @@ bastienbc Bob Killen Brad Fitzpatrick Bruce Downs +Cédric Blomart Chris Marchesi Christian Höltje Clint Greenwood -Cédric Blomart Danny Lockard Dave Tucker -David Stark Davide Agnello +David Stark +Deric Crago Doug MacEachern Eloy Coto Eric Gray @@ -52,11 +55,14 @@ Jason Kincl Jeremy Canady jeremy-clerc João Pereira +Jorge Sevilla +leslie-qiwa Louie Jiang Marc Carmier Matthew Cosgrove Mevan Samaratunga Nicolas Lamirault +Omar Kohl Parham Alvani Pieter Noordhuis runner.mei diff --git a/vendor/github.com/vmware/govmomi/Makefile b/vendor/github.com/vmware/govmomi/Makefile index e0e03ecd3f5..8f07f5e78d9 100644 --- a/vendor/github.com/vmware/govmomi/Makefile +++ b/vendor/github.com/vmware/govmomi/Makefile @@ -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 .) diff --git a/vendor/github.com/vmware/govmomi/README.md b/vendor/github.com/vmware/govmomi/README.md index 02901ea4fba..08bc8df8089 100644 --- a/vendor/github.com/vmware/govmomi/README.md +++ b/vendor/github.com/vmware/govmomi/README.md @@ -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 diff --git a/vendor/github.com/vmware/govmomi/object/cluster_compute_resource.go b/vendor/github.com/vmware/govmomi/object/cluster_compute_resource.go index c9fe3aa035b..24c346825ad 100644 --- a/vendor/github.com/vmware/govmomi/object/cluster_compute_resource.go +++ b/vendor/github.com/vmware/govmomi/object/cluster_compute_resource.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/object/virtual_disk_manager.go b/vendor/github.com/vmware/govmomi/object/virtual_disk_manager.go index 7bea49d51e2..72439caf9c0 100644 --- a/vendor/github.com/vmware/govmomi/object/virtual_disk_manager.go +++ b/vendor/github.com/vmware/govmomi/object/virtual_disk_manager.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/object/virtual_machine.go b/vendor/github.com/vmware/govmomi/object/virtual_machine.go index 511f5572357..3cd1b40b658 100644 --- a/vendor/github.com/vmware/govmomi/object/virtual_machine.go +++ b/vendor/github.com/vmware/govmomi/object/virtual_machine.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/property/collector.go b/vendor/github.com/vmware/govmomi/property/collector.go index ccf712cf9d3..80e5dfbe3ff 100644 --- a/vendor/github.com/vmware/govmomi/property/collector.go +++ b/vendor/github.com/vmware/govmomi/property/collector.go @@ -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) diff --git a/vendor/github.com/vmware/govmomi/property/wait.go b/vendor/github.com/vmware/govmomi/property/wait.go index fe847926ce9..f730525ca70 100644 --- a/vendor/github.com/vmware/govmomi/property/wait.go +++ b/vendor/github.com/vmware/govmomi/property/wait.go @@ -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 } diff --git a/vendor/github.com/vmware/govmomi/simulator/dvs.go b/vendor/github.com/vmware/govmomi/simulator/dvs.go index 8c9b6d63a80..c3d656b4c5b 100644 --- a/vendor/github.com/vmware/govmomi/simulator/dvs.go +++ b/vendor/github.com/vmware/govmomi/simulator/dvs.go @@ -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{} } diff --git a/vendor/github.com/vmware/govmomi/simulator/esx/event_manager.go b/vendor/github.com/vmware/govmomi/simulator/esx/event_manager.go index 81995c4d00f..bacd83b150d 100644 --- a/vendor/github.com/vmware/govmomi/simulator/esx/event_manager.go +++ b/vendor/github.com/vmware/govmomi/simulator/esx/event_manager.go @@ -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", diff --git a/vendor/github.com/vmware/govmomi/simulator/event_manager.go b/vendor/github.com/vmware/govmomi/simulator/event_manager.go index d68f763108f..75316d617df 100644 --- a/vendor/github.com/vmware/govmomi/simulator/event_manager.go +++ b/vendor/github.com/vmware/govmomi/simulator/event_manager.go @@ -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{ diff --git a/vendor/github.com/vmware/govmomi/simulator/os_unix.go b/vendor/github.com/vmware/govmomi/simulator/os_unix.go index 589ae1b57a8..30ea88e921e 100644 --- a/vendor/github.com/vmware/govmomi/simulator/os_unix.go +++ b/vendor/github.com/vmware/govmomi/simulator/os_unix.go @@ -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 } diff --git a/vendor/github.com/vmware/govmomi/simulator/property_collector.go b/vendor/github.com/vmware/govmomi/simulator/property_collector.go index 41d8cef6333..0d985014460 100644 --- a/vendor/github.com/vmware/govmomi/simulator/property_collector.go +++ b/vendor/github.com/vmware/govmomi/simulator/property_collector.go @@ -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. diff --git a/vendor/github.com/vmware/govmomi/simulator/property_filter.go b/vendor/github.com/vmware/govmomi/simulator/property_filter.go index 99a74fb1c13..c24663548da 100644 --- a/vendor/github.com/vmware/govmomi/simulator/property_filter.go +++ b/vendor/github.com/vmware/govmomi/simulator/property_filter.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/simulator/registry.go b/vendor/github.com/vmware/govmomi/simulator/registry.go index 904b549cf10..f9c760025f5 100644 --- a/vendor/github.com/vmware/govmomi/simulator/registry.go +++ b/vendor/github.com/vmware/govmomi/simulator/registry.go @@ -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() {} diff --git a/vendor/github.com/vmware/govmomi/simulator/search_index.go b/vendor/github.com/vmware/govmomi/simulator/search_index.go index f4786f02d24..6940171adf3 100644 --- a/vendor/github.com/vmware/govmomi/simulator/search_index.go +++ b/vendor/github.com/vmware/govmomi/simulator/search_index.go @@ -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 { diff --git a/vendor/github.com/vmware/govmomi/simulator/session_manager.go b/vendor/github.com/vmware/govmomi/simulator/session_manager.go index 60a56c47d42..7039b7efc80 100644 --- a/vendor/github.com/vmware/govmomi/simulator/session_manager.go +++ b/vendor/github.com/vmware/govmomi/simulator/session_manager.go @@ -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, diff --git a/vendor/github.com/vmware/govmomi/simulator/simulator.go b/vendor/github.com/vmware/govmomi/simulator/simulator.go index a5ac4d985b1..4b884283fbd 100644 --- a/vendor/github.com/vmware/govmomi/simulator/simulator.go +++ b/vendor/github.com/vmware/govmomi/simulator/simulator.go @@ -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 } diff --git a/vendor/github.com/vmware/govmomi/simulator/task.go b/vendor/github.com/vmware/govmomi/simulator/task.go index d6dd5bc415f..3b5939c55be 100644 --- a/vendor/github.com/vmware/govmomi/simulator/task.go +++ b/vendor/github.com/vmware/govmomi/simulator/task.go @@ -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 } diff --git a/vendor/github.com/vmware/govmomi/simulator/task_manager.go b/vendor/github.com/vmware/govmomi/simulator/task_manager.go index df271082596..c4f3412bc0f 100644 --- a/vendor/github.com/vmware/govmomi/simulator/task_manager.go +++ b/vendor/github.com/vmware/govmomi/simulator/task_manager.go @@ -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) {} diff --git a/vendor/github.com/vmware/govmomi/simulator/view_manager.go b/vendor/github.com/vmware/govmomi/simulator/view_manager.go index 78a5dadb174..133d9ff5655 100644 --- a/vendor/github.com/vmware/govmomi/simulator/view_manager.go +++ b/vendor/github.com/vmware/govmomi/simulator/view_manager.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/simulator/virtual_disk_manager.go b/vendor/github.com/vmware/govmomi/simulator/virtual_disk_manager.go index ffe5a3e70e2..42e3f58272a 100644 --- a/vendor/github.com/vmware/govmomi/simulator/virtual_disk_manager.go +++ b/vendor/github.com/vmware/govmomi/simulator/virtual_disk_manager.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/simulator/virtual_machine.go b/vendor/github.com/vmware/govmomi/simulator/virtual_machine.go index 446176e3ef1..d6e51f39a5a 100644 --- a/vendor/github.com/vmware/govmomi/simulator/virtual_machine.go +++ b/vendor/github.com/vmware/govmomi/simulator/virtual_machine.go @@ -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 diff --git a/vendor/github.com/vmware/govmomi/vapi/tags/BUILD b/vendor/github.com/vmware/govmomi/vapi/tags/BUILD new file mode 100644 index 00000000000..6e0bd91114a --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vapi/tags/BUILD @@ -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"], +) diff --git a/vendor/github.com/vmware/govmomi/vapi/tags/categories.go b/vendor/github.com/vmware/govmomi/vapi/tags/categories.go new file mode 100644 index 00000000000..ab23b1b8b98 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vapi/tags/categories.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/vapi/tags/rest_client.go b/vendor/github.com/vmware/govmomi/vapi/tags/rest_client.go new file mode 100644 index 00000000000..6c70ae69cb9 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vapi/tags/rest_client.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/vapi/tags/tag_association.go b/vendor/github.com/vmware/govmomi/vapi/tags/tag_association.go new file mode 100755 index 00000000000..3751ee4fe93 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vapi/tags/tag_association.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/vapi/tags/tags.go b/vendor/github.com/vmware/govmomi/vapi/tags/tags.go new file mode 100644 index 00000000000..8a4e8d6d4f7 --- /dev/null +++ b/vendor/github.com/vmware/govmomi/vapi/tags/tags.go @@ -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 +} diff --git a/vendor/github.com/vmware/govmomi/vim25/client.go b/vendor/github.com/vmware/govmomi/vim25/client.go index b6d34c36d73..1d26fb8b6a7 100644 --- a/vendor/github.com/vmware/govmomi/vim25/client.go +++ b/vendor/github.com/vmware/govmomi/vim25/client.go @@ -28,7 +28,7 @@ import ( const ( Namespace = "vim25" - Version = "6.5" + Version = "6.7" Path = "/sdk" ) diff --git a/vendor/github.com/vmware/govmomi/vim25/mo/retrieve.go b/vendor/github.com/vmware/govmomi/vim25/mo/retrieve.go index e7ffc32cec1..c470c0ac0a1 100644 --- a/vendor/github.com/vmware/govmomi/vim25/mo/retrieve.go +++ b/vendor/github.com/vmware/govmomi/vim25/mo/retrieve.go @@ -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 { diff --git a/vendor/github.com/vmware/govmomi/vim25/mo/type_info.go b/vendor/github.com/vmware/govmomi/vim25/mo/type_info.go index 0c9e5b03488..2228e53a0cc 100644 --- a/vendor/github.com/vmware/govmomi/vim25/mo/type_info.go +++ b/vendor/github.com/vmware/govmomi/vim25/mo/type_info.go @@ -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 diff --git a/vendor/github.com/vmware/govmomi/vim25/soap/client.go b/vendor/github.com/vmware/govmomi/vim25/soap/client.go index d94b6a05658..7553a83d85b 100644 --- a/vendor/github.com/vmware/govmomi/vim25/soap/client.go +++ b/vendor/github.com/vmware/govmomi/vim25/soap/client.go @@ -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 diff --git a/vendor/github.com/vmware/govmomi/vim25/soap/error.go b/vendor/github.com/vmware/govmomi/vim25/soap/error.go index d89208522a9..46111556cbd 100644 --- a/vendor/github.com/vmware/govmomi/vim25/soap/error.go +++ b/vendor/github.com/vmware/govmomi/vim25/soap/error.go @@ -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)