Commit c340f6f9 authored by Doug MacEachern's avatar Doug MacEachern

vSphere Cloud Provider: update vmware/gomvomi godeps

parent 83768d28
...@@ -2677,113 +2677,148 @@ ...@@ -2677,113 +2677,148 @@
}, },
{ {
"ImportPath": "github.com/vmware/govmomi", "ImportPath": "github.com/vmware/govmomi",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/find", "ImportPath": "github.com/vmware/govmomi/find",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/list", "ImportPath": "github.com/vmware/govmomi/list",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/lookup",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/lookup/methods",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/lookup/simulator",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/lookup/types",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/nfc", "ImportPath": "github.com/vmware/govmomi/nfc",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/object", "ImportPath": "github.com/vmware/govmomi/object",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/pbm", "ImportPath": "github.com/vmware/govmomi/pbm",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/pbm/methods", "ImportPath": "github.com/vmware/govmomi/pbm/methods",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/pbm/types", "ImportPath": "github.com/vmware/govmomi/pbm/types",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/property", "ImportPath": "github.com/vmware/govmomi/property",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/session", "ImportPath": "github.com/vmware/govmomi/session",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/simulator", "ImportPath": "github.com/vmware/govmomi/simulator",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/simulator/esx", "ImportPath": "github.com/vmware/govmomi/simulator/esx",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/simulator/vpx", "ImportPath": "github.com/vmware/govmomi/simulator/vpx",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/sts",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/sts/internal",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/sts/simulator",
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/task", "ImportPath": "github.com/vmware/govmomi/task",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25", "ImportPath": "github.com/vmware/govmomi/vim25",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/debug", "ImportPath": "github.com/vmware/govmomi/vim25/debug",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/methods", "ImportPath": "github.com/vmware/govmomi/vim25/methods",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/mo", "ImportPath": "github.com/vmware/govmomi/vim25/mo",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/progress", "ImportPath": "github.com/vmware/govmomi/vim25/progress",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/soap", "ImportPath": "github.com/vmware/govmomi/vim25/soap",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/types", "ImportPath": "github.com/vmware/govmomi/vim25/types",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/xml", "ImportPath": "github.com/vmware/govmomi/vim25/xml",
"Comment": "v0.16.0-97-g0f82f03", "Comment": "v0.17.1-46-ge70dd44",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc" "Rev": "e70dd44f80baf671099254d675eb278529038234"
}, },
{ {
"ImportPath": "github.com/vmware/photon-controller-go-sdk/SSPI", "ImportPath": "github.com/vmware/photon-controller-go-sdk/SSPI",
......
---
project_name: govc
builds:
- goos:
- linux
- darwin
- windows
- freebsd
goarch:
- amd64
- 386
env:
- CGO_ENABLED=0
main: ./govc/main.go
binary: govc
flags: -compiler gc
ldflags: -X github.com/vmware/govmomi/govc/flags.GitVersion={{.Version}}
archive:
name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}'
format: tar.gz
format_overrides:
- goos: windows
format: zip
files:
- none*
checksum:
name_template: '{{ .ProjectName }}_{{ .Version }}_checksums.txt'
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- Merge pull request
- Merge branch
brew:
github:
owner: govmomi
name: homebrew-tap
commit_author:
name: Alfred the Narwhal
email: cna-alfred@vmware.com
folder: Formula
homepage: "https://github.com/vmware/govmomi/blob/master/govc/README.md"
description: "govc is a vSphere CLI built on top of govmomi."
test: |
system "#{bin}/govc version"
dockers:
- image: vmware/govc
goos: linux
goarch: amd64
binary: govc
tag_templates:
- "{{ .Tag }}"
- "v{{ .Major }}"
- "v{{ .Major }}.{{ .Minor }}"
- latest
...@@ -3,11 +3,28 @@ sudo: false ...@@ -3,11 +3,28 @@ sudo: false
language: go language: go
go: go:
- 1.8 - 1.8.x
- 1.9.x
- '1.10'
go_import_path: github.com/vmware/govmomi
before_install: before_install:
- sudo apt-get -qq update
- sudo apt-get install -y xmlstarlet
- make vendor - make vendor
script: script:
- make check test - make check test
- GOOS=windows make install - GOOS=windows make install
after_success:
- test -n "$TRAVIS_TAG" && docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
deploy:
- provider: script
skip_cleanup: true
script: curl -sL http://git.io/goreleaser | bash
on:
tags: true
condition: $TRAVIS_OS_NAME = linux
go: '1.10'
\ No newline at end of file
# changelog # changelog
### 0.17.1 (2018-03-19)
* vcsim: add Destroy method for Folder and Datacenter types
* In progress.Reader emit final report on EOF.
* vcsim: add EventManager.QueryEvents
### 0.17.0 (2018-02-28)
* Add HostStorageSystem.AttachScsiLun method
* Avoid possible panic in Datastore.Stat (#969)
* Destroy event history collectors (#962)
* Add VirtualDiskManager.CreateChildDisk method
### 0.16.0 (2017-11-08) ### 0.16.0 (2017-11-08)
* Add support for SOAP request operation ID header * Add support for SOAP request operation ID header
......
...@@ -6,8 +6,10 @@ ...@@ -6,8 +6,10 @@
Abhijeet Kasurde <akasurde@redhat.com> Abhijeet Kasurde <akasurde@redhat.com>
abrarshivani <abrarshivani@users.noreply.github.com> abrarshivani <abrarshivani@users.noreply.github.com>
Adam Shannon <adamkshannon@gmail.com> Adam Shannon <adamkshannon@gmail.com>
Alessandro Cortiana <alessandro.cortiana@gmail.com>
Alex Bozhenko <alexbozhenko@fb.com>
Alvaro Miranda <kikitux@gmail.com> Alvaro Miranda <kikitux@gmail.com>
amandahla <amanda.andrade@serpro.gov.br> Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br>
Amit Bathla <abathla@.vmware.com> Amit Bathla <abathla@.vmware.com>
amit bezalel <amit.bezalel@hpe.com> amit bezalel <amit.bezalel@hpe.com>
Andrew Chin <andrew@andrewtchin.com> Andrew Chin <andrew@andrewtchin.com>
...@@ -21,17 +23,19 @@ bastienbc <bastien.barbe.creuly@gmail.com> ...@@ -21,17 +23,19 @@ bastienbc <bastien.barbe.creuly@gmail.com>
Bob Killen <killen.bob@gmail.com> Bob Killen <killen.bob@gmail.com>
Brad Fitzpatrick <bradfitz@golang.org> Brad Fitzpatrick <bradfitz@golang.org>
Bruce Downs <bruceadowns@gmail.com> Bruce Downs <bruceadowns@gmail.com>
Cédric Blomart <cblomart@gmail.com> Chris Marchesi <chrism@vancluevertech.com>
Christian Höltje <docwhat@gerf.org> Christian Höltje <docwhat@gerf.org>
Clint Greenwood <cgreenwood@vmware.com> Clint Greenwood <cgreenwood@vmware.com>
Cédric Blomart <cblomart@gmail.com>
Danny Lockard <danny.lockard@banno.com> Danny Lockard <danny.lockard@banno.com>
Dave Tucker <dave@dtucker.co.uk> Dave Tucker <dave@dtucker.co.uk>
Davide Agnello <dagnello@hp.com>
David Stark <dave@davidstark.name> David Stark <dave@davidstark.name>
Davide Agnello <dagnello@hp.com>
Doug MacEachern <dougm@vmware.com> Doug MacEachern <dougm@vmware.com>
Eloy Coto <eloy.coto@gmail.com> Eloy Coto <eloy.coto@gmail.com>
Eric Gray <egray@vmware.com> Eric Gray <egray@vmware.com>
Eric Yutao <eric.yutao@gmail.com> Eric Yutao <eric.yutao@gmail.com>
Erik Hollensbe <github@hollensbe.org>
Fabio Rapposelli <fabio@vmware.com> Fabio Rapposelli <fabio@vmware.com>
Faiyaz Ahmed <ahmedf@vmware.com> Faiyaz Ahmed <ahmedf@vmware.com>
forkbomber <forkbomber@users.noreply.github.com> forkbomber <forkbomber@users.noreply.github.com>
...@@ -46,11 +50,14 @@ Isaac Rodman <isaac@eyz.us> ...@@ -46,11 +50,14 @@ Isaac Rodman <isaac@eyz.us>
Ivan Porto Carrero <icarrero@vmware.com> Ivan Porto Carrero <icarrero@vmware.com>
Jason Kincl <jkincl@gmail.com> Jason Kincl <jkincl@gmail.com>
Jeremy Canady <jcanady@jackhenry.com> Jeremy Canady <jcanady@jackhenry.com>
jeremy-clerc <jeremy@clerc.io>
João Pereira <joaodrp@gmail.com> João Pereira <joaodrp@gmail.com>
Louie Jiang <jiangl@vmware.com> Louie Jiang <jiangl@vmware.com>
Marc Carmier <mcarmier@gmail.com> Marc Carmier <mcarmier@gmail.com>
Matthew Cosgrove <matthew.cosgrove@dell.com>
Mevan Samaratunga <mevansam@gmail.com> Mevan Samaratunga <mevansam@gmail.com>
Nicolas Lamirault <nicolas.lamirault@gmail.com> Nicolas Lamirault <nicolas.lamirault@gmail.com>
Parham Alvani <parham.alvani@gmail.com>
Pieter Noordhuis <pnoordhuis@vmware.com> Pieter Noordhuis <pnoordhuis@vmware.com>
runner.mei <runner.mei@gmail.com> runner.mei <runner.mei@gmail.com>
S.Çağlar Onur <conur@vmware.com> S.Çağlar Onur <conur@vmware.com>
...@@ -60,7 +67,10 @@ Takaaki Furukawa <takaaki.frkw@gmail.com> ...@@ -60,7 +67,10 @@ Takaaki Furukawa <takaaki.frkw@gmail.com>
tanishi <tanishi503@gmail.com> tanishi <tanishi503@gmail.com>
Ted Zlatanov <tzz@lifelogs.com> Ted Zlatanov <tzz@lifelogs.com>
Thibaut Ackermann <thibaut.ackermann@alcatel-lucent.com> Thibaut Ackermann <thibaut.ackermann@alcatel-lucent.com>
Trevor Dawe <trevor.dawe@gmail.com>
Vadim Egorov <vegorov@vmware.com> Vadim Egorov <vegorov@vmware.com>
Volodymyr Bobyr <pupsua@gmail.com>
Witold Krecicki <wpk@culm.net>
Yang Yang <yangy@vmware.com> Yang Yang <yangy@vmware.com>
Yuya Kusakabe <yuya.kusakabe@gmail.com> Yuya Kusakabe <yuya.kusakabe@gmail.com>
Zach Tucker <ztucker@vmware.com> Zach Tucker <ztucker@vmware.com>
......
FROM scratch
LABEL maintainer="fabio@vmware.com"
COPY govc /
ENTRYPOINT [ "/govc" ]
\ No newline at end of file
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "improvements"
name = "github.com/davecgh/go-xdr"
packages = ["xdr2"]
revision = "4930550ba2e22f87187498acfd78348b15f4e7a8"
source = "https://github.com/rasky/go-xdr"
[[projects]]
name = "github.com/google/uuid"
packages = ["."]
revision = "6a5e28554805e78ea6141142aba763936c4761c0"
[[projects]]
branch = "govmomi"
name = "github.com/kr/pretty"
packages = ["."]
revision = "2ee9d7453c02ef7fa518a83ae23644eb8872186a"
source = "https://github.com/dougm/pretty"
[[projects]]
branch = "master"
name = "github.com/kr/text"
packages = ["."]
revision = "7cafcd837844e784b526369c9bce262804aebc60"
[[projects]]
branch = "master"
name = "github.com/vmware/vmw-guestinfo"
packages = [
"bdoor",
"message",
"vmcheck"
]
revision = "25eff159a728be87e103a0b8045e08273f4dbec4"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "376638fa6c0621cbd980caf8fc53494d880886f100663da8de47ecb6e596e439"
solver-name = "gps-cdcl"
solver-version = 1
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# Refer to https://github.com/toml-lang/toml for detailed TOML docs.
[prune]
non-go = true
go-tests = true
unused-packages = true
[[constraint]]
branch = "improvements"
name = "github.com/davecgh/go-xdr"
source = "https://github.com/rasky/go-xdr"
[[constraint]]
branch = "govmomi"
name = "github.com/kr/pretty"
source = "https://github.com/dougm/pretty"
...@@ -58,7 +58,6 @@ package govmomi ...@@ -58,7 +58,6 @@ package govmomi
import ( import (
"context" "context"
"crypto/tls"
"net/url" "net/url"
"github.com/vmware/govmomi/property" "github.com/vmware/govmomi/property"
...@@ -99,41 +98,11 @@ func NewClient(ctx context.Context, u *url.URL, insecure bool) (*Client, error) ...@@ -99,41 +98,11 @@ func NewClient(ctx context.Context, u *url.URL, insecure bool) (*Client, error)
return c, nil return c, nil
} }
// NewClientWithCertificate creates a new client from a URL. The client authenticates with the
// server with the certificate before returning if the URL contains user information.
func NewClientWithCertificate(ctx context.Context, u *url.URL, insecure bool, cert tls.Certificate) (*Client, error) {
soapClient := soap.NewClient(u, insecure)
soapClient.SetCertificate(cert)
vimClient, err := vim25.NewClient(ctx, soapClient)
if err != nil {
return nil, err
}
c := &Client{
Client: vimClient,
SessionManager: session.NewManager(vimClient),
}
if u.User != nil {
err = c.LoginExtensionByCertificate(ctx, u.User.Username(), "")
if err != nil {
return nil, err
}
}
return c, nil
}
// Login dispatches to the SessionManager. // Login dispatches to the SessionManager.
func (c *Client) Login(ctx context.Context, u *url.Userinfo) error { func (c *Client) Login(ctx context.Context, u *url.Userinfo) error {
return c.SessionManager.Login(ctx, u) return c.SessionManager.Login(ctx, u)
} }
// Login dispatches to the SessionManager.
func (c *Client) LoginExtensionByCertificate(ctx context.Context, key string, locale string) error {
return c.SessionManager.LoginExtensionByCertificate(ctx, key, locale)
}
// Logout dispatches to the SessionManager. // Logout dispatches to the SessionManager.
func (c *Client) Logout(ctx context.Context) error { func (c *Client) Logout(ctx context.Context) error {
// Close any idle connections after logging out. // Close any idle connections after logging out.
......
...@@ -716,7 +716,7 @@ func (f *Finder) HostSystem(ctx context.Context, path string) (*object.HostSyste ...@@ -716,7 +716,7 @@ func (f *Finder) HostSystem(ctx context.Context, path string) (*object.HostSyste
} }
func (f *Finder) DefaultHostSystem(ctx context.Context) (*object.HostSystem, error) { func (f *Finder) DefaultHostSystem(ctx context.Context) (*object.HostSystem, error) {
hs, err := f.HostSystem(ctx, "*/*") hs, err := f.HostSystem(ctx, "*")
if err != nil { if err != nil {
return nil, toDefaultError(err) return nil, toDefaultError(err)
} }
......
/*
Copyright (c) 2018 VMware, Inc. All 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 lookup
import (
"context"
"crypto/x509"
"encoding/base64"
"log"
"net/url"
"github.com/vmware/govmomi/lookup/methods"
"github.com/vmware/govmomi/lookup/types"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/soap"
vim "github.com/vmware/govmomi/vim25/types"
)
const (
Namespace = "lookup"
Version = "2.0"
Path = "/lookupservice" + vim25.Path
)
var (
ServiceInstance = vim.ManagedObjectReference{
Type: "LookupServiceInstance",
Value: "ServiceInstance",
}
)
// Client is a soap.Client targeting the SSO Lookup Service API endpoint.
type Client struct {
*soap.Client
ServiceContent types.LookupServiceContent
}
// NewClient returns a client targeting the SSO Lookup Service API endpoint.
func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
sc := c.Client.NewServiceClient(Path, Namespace)
sc.Version = Version
req := types.RetrieveServiceContent{
This: ServiceInstance,
}
res, err := methods.RetrieveServiceContent(ctx, sc, &req)
if err != nil {
return nil, err
}
return &Client{sc, res.Returnval}, nil
}
func (c *Client) List(ctx context.Context, filter *types.LookupServiceRegistrationFilter) ([]types.LookupServiceRegistrationInfo, error) {
req := types.List{
This: *c.ServiceContent.ServiceRegistration,
FilterCriteria: filter,
}
res, err := methods.List(ctx, c, &req)
if err != nil {
return nil, err
}
return res.Returnval, nil
}
func (c *Client) SiteID(ctx context.Context) (string, error) {
req := types.GetSiteId{
This: *c.ServiceContent.ServiceRegistration,
}
res, err := methods.GetSiteId(ctx, c, &req)
if err != nil {
return "", err
}
return res.Returnval, nil
}
// EndpointURL uses the Lookup Service to find the endpoint URL and thumbprint for the given filter.
// If the endpoint is found, its TLS certificate is also added to the vim25.Client's trusted host thumbprints.
// If the Lookup Service is not available, the given path is returned as the default.
func EndpointURL(ctx context.Context, c *vim25.Client, path string, filter *types.LookupServiceRegistrationFilter) string {
if lu, err := NewClient(ctx, c); err == nil {
info, _ := lu.List(ctx, filter)
if len(info) != 0 && len(info[0].ServiceEndpoints) != 0 {
endpoint := &info[0].ServiceEndpoints[0]
path = endpoint.Url
if u, err := url.Parse(path); err == nil {
if c.Thumbprint(u.Host) == "" {
c.SetThumbprint(u.Host, endpointThumbprint(endpoint))
}
}
}
}
return path
}
// endpointThumbprint converts the base64 encoded endpoint certificate to a SHA1 thumbprint.
func endpointThumbprint(endpoint *types.LookupServiceRegistrationEndpoint) string {
if len(endpoint.SslTrust) == 0 {
return ""
}
enc := endpoint.SslTrust[0]
b, err := base64.StdEncoding.DecodeString(enc)
if err != nil {
log.Printf("base64.Decode(%q): %s", enc, err)
return ""
}
cert, err := x509.ParseCertificate(b)
if err != nil {
log.Printf("x509.ParseCertificate(%q): %s", enc, err)
return ""
}
return soap.ThumbprintSHA1(cert)
}
/*
Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package methods
import (
"context"
"github.com/vmware/govmomi/lookup/types"
"github.com/vmware/govmomi/vim25/soap"
)
type CreateBody struct {
Req *types.Create `xml:"urn:lookup Create,omitempty"`
Res *types.CreateResponse `xml:"urn:lookup CreateResponse,omitempty"`
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *CreateBody) Fault() *soap.Fault { return b.Fault_ }
func Create(ctx context.Context, r soap.RoundTripper, req *types.Create) (*types.CreateResponse, error) {
var reqBody, resBody CreateBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type DeleteBody struct {
Req *types.Delete `xml:"urn:lookup Delete,omitempty"`
Res *types.DeleteResponse `xml:"urn:lookup DeleteResponse,omitempty"`
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *DeleteBody) Fault() *soap.Fault { return b.Fault_ }
func Delete(ctx context.Context, r soap.RoundTripper, req *types.Delete) (*types.DeleteResponse, error) {
var reqBody, resBody DeleteBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type GetBody struct {
Req *types.Get `xml:"urn:lookup Get,omitempty"`
Res *types.GetResponse `xml:"urn:lookup GetResponse,omitempty"`
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *GetBody) Fault() *soap.Fault { return b.Fault_ }
func Get(ctx context.Context, r soap.RoundTripper, req *types.Get) (*types.GetResponse, error) {
var reqBody, resBody GetBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type GetLocaleBody struct {
Req *types.GetLocale `xml:"urn:lookup GetLocale,omitempty"`
Res *types.GetLocaleResponse `xml:"urn:lookup GetLocaleResponse,omitempty"`
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *GetLocaleBody) Fault() *soap.Fault { return b.Fault_ }
func GetLocale(ctx context.Context, r soap.RoundTripper, req *types.GetLocale) (*types.GetLocaleResponse, error) {
var reqBody, resBody GetLocaleBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type GetSiteIdBody struct {
Req *types.GetSiteId `xml:"urn:lookup GetSiteId,omitempty"`
Res *types.GetSiteIdResponse `xml:"urn:lookup GetSiteIdResponse,omitempty"`
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *GetSiteIdBody) Fault() *soap.Fault { return b.Fault_ }
func GetSiteId(ctx context.Context, r soap.RoundTripper, req *types.GetSiteId) (*types.GetSiteIdResponse, error) {
var reqBody, resBody GetSiteIdBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type ListBody struct {
Req *types.List `xml:"urn:lookup List,omitempty"`
Res *types.ListResponse `xml:"urn:lookup ListResponse,omitempty"`
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *ListBody) Fault() *soap.Fault { return b.Fault_ }
func List(ctx context.Context, r soap.RoundTripper, req *types.List) (*types.ListResponse, error) {
var reqBody, resBody ListBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type RetrieveHaBackupConfigurationBody struct {
Req *types.RetrieveHaBackupConfiguration `xml:"urn:lookup RetrieveHaBackupConfiguration,omitempty"`
Res *types.RetrieveHaBackupConfigurationResponse `xml:"urn:lookup RetrieveHaBackupConfigurationResponse,omitempty"`
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *RetrieveHaBackupConfigurationBody) Fault() *soap.Fault { return b.Fault_ }
func RetrieveHaBackupConfiguration(ctx context.Context, r soap.RoundTripper, req *types.RetrieveHaBackupConfiguration) (*types.RetrieveHaBackupConfigurationResponse, error) {
var reqBody, resBody RetrieveHaBackupConfigurationBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type RetrieveServiceContentBody struct {
Req *types.RetrieveServiceContent `xml:"urn:lookup RetrieveServiceContent,omitempty"`
Res *types.RetrieveServiceContentResponse `xml:"urn:lookup RetrieveServiceContentResponse,omitempty"`
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *RetrieveServiceContentBody) Fault() *soap.Fault { return b.Fault_ }
func RetrieveServiceContent(ctx context.Context, r soap.RoundTripper, req *types.RetrieveServiceContent) (*types.RetrieveServiceContentResponse, error) {
var reqBody, resBody RetrieveServiceContentBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type SetBody struct {
Req *types.Set `xml:"urn:lookup Set,omitempty"`
Res *types.SetResponse `xml:"urn:lookup SetResponse,omitempty"`
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *SetBody) Fault() *soap.Fault { return b.Fault_ }
func Set(ctx context.Context, r soap.RoundTripper, req *types.Set) (*types.SetResponse, error) {
var reqBody, resBody SetBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type SetLocaleBody struct {
Req *types.SetLocale `xml:"urn:lookup SetLocale,omitempty"`
Res *types.SetLocaleResponse `xml:"urn:lookup SetLocaleResponse,omitempty"`
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *SetLocaleBody) Fault() *soap.Fault { return b.Fault_ }
func SetLocale(ctx context.Context, r soap.RoundTripper, req *types.SetLocale) (*types.SetLocaleResponse, error) {
var reqBody, resBody SetLocaleBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/google/uuid"
"github.com/vmware/govmomi/lookup"
"github.com/vmware/govmomi/lookup/types"
"github.com/vmware/govmomi/simulator"
"github.com/vmware/govmomi/vim25"
)
var (
siteID = "vcsim"
)
// registrationInfo returns a ServiceRegistration populated with vcsim's OptionManager settings.
// The complete list can be captured using: govc sso.service.ls -dump
func registrationInfo() []types.LookupServiceRegistrationInfo {
vc := simulator.Map.Get(vim25.ServiceInstance).(*simulator.ServiceInstance)
setting := simulator.Map.OptionManager().Setting
opts := make(map[string]string, len(setting))
for _, o := range setting {
opt := o.GetOptionValue()
if val, ok := opt.Value.(string); ok {
opts[opt.Key] = val
}
}
trust := []string{opts["vcsim.server.cert"]}
sdk := opts["vcsim.server.url"] + vim25.Path
admin := opts["config.vpxd.sso.default.admin"]
owner := opts["config.vpxd.sso.solutionUser.name"]
instance := opts["VirtualCenter.InstanceName"]
// Real PSC has 30+ services by default, we just provide a few that are useful for vmomi interaction..
return []types.LookupServiceRegistrationInfo{
{
LookupServiceRegistrationCommonServiceInfo: types.LookupServiceRegistrationCommonServiceInfo{
LookupServiceRegistrationMutableServiceInfo: types.LookupServiceRegistrationMutableServiceInfo{
ServiceVersion: lookup.Version,
ServiceEndpoints: []types.LookupServiceRegistrationEndpoint{
{
Url: opts["config.vpxd.sso.sts.uri"],
EndpointType: types.LookupServiceRegistrationEndpointType{
Protocol: "wsTrust",
Type: "com.vmware.cis.cs.identity.sso",
},
SslTrust: trust,
},
},
},
OwnerId: admin,
ServiceType: types.LookupServiceRegistrationServiceType{
Product: "com.vmware.cis",
Type: "sso:sts",
},
},
ServiceId: siteID + ":" + uuid.New().String(),
SiteId: siteID,
},
{
LookupServiceRegistrationCommonServiceInfo: types.LookupServiceRegistrationCommonServiceInfo{
LookupServiceRegistrationMutableServiceInfo: types.LookupServiceRegistrationMutableServiceInfo{
ServiceVersion: vim25.Version,
ServiceEndpoints: []types.LookupServiceRegistrationEndpoint{
{
Url: sdk,
EndpointType: types.LookupServiceRegistrationEndpointType{
Protocol: "vmomi",
Type: "com.vmware.vim",
},
SslTrust: trust,
EndpointAttributes: []types.LookupServiceRegistrationAttribute{
{
Key: "cis.common.ep.localurl",
Value: sdk,
},
},
},
},
ServiceAttributes: []types.LookupServiceRegistrationAttribute{
{
Key: "com.vmware.cis.cm.GroupInternalId",
Value: "com.vmware.vim.vcenter",
},
{
Key: "com.vmware.vim.vcenter.instanceName",
Value: instance,
},
{
Key: "com.vmware.cis.cm.ControlScript",
Value: "service-control-default-vmon",
},
{
Key: "com.vmware.cis.cm.HostId",
Value: uuid.New().String(),
},
},
ServiceNameResourceKey: "AboutInfo.vpx.name",
ServiceDescriptionResourceKey: "AboutInfo.vpx.name",
},
OwnerId: owner,
ServiceType: types.LookupServiceRegistrationServiceType{
Product: "com.vmware.cis",
Type: "vcenterserver",
},
NodeId: uuid.New().String(),
},
ServiceId: vc.Content.About.InstanceUuid,
SiteId: siteID,
},
}
}
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/lookup"
"github.com/vmware/govmomi/lookup/methods"
"github.com/vmware/govmomi/lookup/types"
"github.com/vmware/govmomi/simulator"
"github.com/vmware/govmomi/vim25/soap"
vim "github.com/vmware/govmomi/vim25/types"
)
var content = types.LookupServiceContent{
LookupService: vim.ManagedObjectReference{Type: "LookupLookupService", Value: "lookupService"},
ServiceRegistration: &vim.ManagedObjectReference{Type: "LookupServiceRegistration", Value: "ServiceRegistration"},
DeploymentInformationService: vim.ManagedObjectReference{Type: "LookupDeploymentInformationService", Value: "deploymentInformationService"},
L10n: vim.ManagedObjectReference{Type: "LookupL10n", Value: "l10n"},
}
func New() *simulator.Registry {
r := simulator.NewRegistry()
r.Namespace = lookup.Namespace
r.Path = lookup.Path
r.Put(&ServiceInstance{
ManagedObjectReference: lookup.ServiceInstance,
Content: content,
})
r.Put(&ServiceRegistration{
ManagedObjectReference: *content.ServiceRegistration,
Info: registrationInfo(),
})
return r
}
type ServiceInstance struct {
vim.ManagedObjectReference
Content types.LookupServiceContent
}
func (s *ServiceInstance) RetrieveServiceContent(_ *types.RetrieveServiceContent) soap.HasFault {
return &methods.RetrieveServiceContentBody{
Res: &types.RetrieveServiceContentResponse{
Returnval: s.Content,
},
}
}
type ServiceRegistration struct {
vim.ManagedObjectReference
Info []types.LookupServiceRegistrationInfo
}
func (s *ServiceRegistration) GetSiteId(_ *types.GetSiteId) soap.HasFault {
return &methods.GetSiteIdBody{
Res: &types.GetSiteIdResponse{
Returnval: siteID,
},
}
}
func matchServiceType(filter, info *types.LookupServiceRegistrationServiceType) bool {
if filter.Product != "" {
if filter.Product != info.Product {
return false
}
}
if filter.Type != "" {
if filter.Type != info.Type {
return false
}
}
return true
}
func matchEndpointType(filter, info *types.LookupServiceRegistrationEndpointType) bool {
if filter.Protocol != "" {
if filter.Protocol != info.Protocol {
return false
}
}
if filter.Type != "" {
if filter.Type != info.Type {
return false
}
}
return true
}
func (s *ServiceRegistration) List(req *types.List) soap.HasFault {
body := new(methods.ListBody)
filter := req.FilterCriteria
if filter == nil {
// This is what a real PSC returns if FilterCriteria is nil.
body.Fault_ = simulator.Fault("LookupFaultServiceFault", &vim.SystemError{
Reason: "Invalid fault",
})
return body
}
body.Res = new(types.ListResponse)
for _, info := range s.Info {
if filter.SiteId != "" {
if filter.SiteId != info.SiteId {
continue
}
}
if filter.NodeId != "" {
if filter.NodeId != info.NodeId {
continue
}
}
if filter.ServiceType != nil {
if !matchServiceType(filter.ServiceType, &info.ServiceType) {
continue
}
}
if filter.EndpointType != nil {
services := info.ServiceEndpoints
info.ServiceEndpoints = nil
for _, service := range services {
if !matchEndpointType(filter.EndpointType, &service.EndpointType) {
continue
}
info.ServiceEndpoints = append(info.ServiceEndpoints, service)
}
if len(info.ServiceEndpoints) == 0 {
continue
}
}
body.Res.Returnval = append(body.Res.Returnval, info)
}
return body
}
...@@ -18,6 +18,7 @@ package object ...@@ -18,6 +18,7 @@ package object
import ( import (
"context" "context"
"fmt"
"github.com/vmware/govmomi/vim25" "github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/methods"
...@@ -39,11 +40,17 @@ func NewDistributedVirtualPortgroup(c *vim25.Client, ref types.ManagedObjectRefe ...@@ -39,11 +40,17 @@ func NewDistributedVirtualPortgroup(c *vim25.Client, ref types.ManagedObjectRefe
func (p DistributedVirtualPortgroup) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) { func (p DistributedVirtualPortgroup) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
var dvp mo.DistributedVirtualPortgroup var dvp mo.DistributedVirtualPortgroup
var dvs mo.DistributedVirtualSwitch var dvs mo.DistributedVirtualSwitch
prop := "config.distributedVirtualSwitch"
if err := p.Properties(ctx, p.Reference(), []string{"key", "config.distributedVirtualSwitch"}, &dvp); err != nil { if err := p.Properties(ctx, p.Reference(), []string{"key", prop}, &dvp); err != nil {
return nil, err return nil, err
} }
// "This property should always be set unless the user's setting does not have System.Read privilege on the object referred to by this property."
if dvp.Config.DistributedVirtualSwitch == nil {
return nil, fmt.Errorf("no System.Read privilege on: %s.%s", p.Reference(), prop)
}
if err := p.Properties(ctx, *dvp.Config.DistributedVirtualSwitch, []string{"uuid"}, &dvs); err != nil { if err := p.Properties(ctx, *dvp.Config.DistributedVirtualSwitch, []string{"uuid"}, &dvs); err != nil {
return nil, err return nil, err
} }
......
...@@ -66,9 +66,10 @@ func (s DistributedVirtualSwitch) AddPortgroup(ctx context.Context, spec []types ...@@ -66,9 +66,10 @@ func (s DistributedVirtualSwitch) AddPortgroup(ctx context.Context, spec []types
return NewTask(s.Client(), res.Returnval), nil return NewTask(s.Client(), res.Returnval), nil
} }
func (s DistributedVirtualSwitch) FetchDVPorts(ctx context.Context) ([]types.DistributedVirtualPort, error) { func (s DistributedVirtualSwitch) FetchDVPorts(ctx context.Context, criteria *types.DistributedVirtualSwitchPortCriteria) ([]types.DistributedVirtualPort, error) {
req := &types.FetchDVPorts{ req := &types.FetchDVPorts{
This: s.Reference(), This: s.Reference(),
Criteria: criteria,
} }
res, err := methods.FetchDVPorts(ctx, s.Client(), req) res, err := methods.FetchDVPorts(ctx, s.Client(), req)
......
...@@ -61,7 +61,10 @@ func EthernetCardTypes() VirtualDeviceList { ...@@ -61,7 +61,10 @@ func EthernetCardTypes() VirtualDeviceList {
return VirtualDeviceList([]types.BaseVirtualDevice{ return VirtualDeviceList([]types.BaseVirtualDevice{
&types.VirtualE1000{}, &types.VirtualE1000{},
&types.VirtualE1000e{}, &types.VirtualE1000e{},
&types.VirtualVmxnet2{},
&types.VirtualVmxnet3{}, &types.VirtualVmxnet3{},
&types.VirtualPCNet32{},
&types.VirtualSriovEthernetCard{},
}).Select(func(device types.BaseVirtualDevice) bool { }).Select(func(device types.BaseVirtualDevice) bool {
c := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard() c := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
c.GetVirtualDevice().Key = -1 c.GetVirtualDevice().Key = -1
...@@ -834,7 +837,7 @@ func (l VirtualDeviceList) TypeName(device types.BaseVirtualDevice) string { ...@@ -834,7 +837,7 @@ func (l VirtualDeviceList) TypeName(device types.BaseVirtualDevice) string {
return dtype.Elem().Name() return dtype.Elem().Name()
} }
var deviceNameRegexp = regexp.MustCompile(`(?:Virtual)?(?:Machine)?(\w+?)(?:Card|Device|Controller)?$`) var deviceNameRegexp = regexp.MustCompile(`(?:Virtual)?(?:Machine)?(\w+?)(?:Card|EthernetCard|Device|Controller)?$`)
func (l VirtualDeviceList) deviceName(device types.BaseVirtualDevice) string { func (l VirtualDeviceList) deviceName(device types.BaseVirtualDevice) string {
name := "device" name := "device"
......
...@@ -46,9 +46,10 @@ type queryVirtualDiskInfoTaskResponse struct { ...@@ -46,9 +46,10 @@ type queryVirtualDiskInfoTaskResponse struct {
} }
type queryVirtualDiskInfoTaskBody struct { type queryVirtualDiskInfoTaskBody struct {
Req *queryVirtualDiskInfoTaskRequest `xml:"urn:internalvim25 QueryVirtualDiskInfo_Task,omitempty"` Req *queryVirtualDiskInfoTaskRequest `xml:"urn:internalvim25 QueryVirtualDiskInfo_Task,omitempty"`
Res *queryVirtualDiskInfoTaskResponse `xml:"urn:vim25 QueryVirtualDiskInfo_TaskResponse,omitempty"` Res *queryVirtualDiskInfoTaskResponse `xml:"urn:vim25 QueryVirtualDiskInfo_TaskResponse,omitempty"`
Err *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` InternalRes *queryVirtualDiskInfoTaskResponse `xml:"urn:internalvim25 QueryVirtualDiskInfo_TaskResponse,omitempty"`
Err *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
} }
func (b *queryVirtualDiskInfoTaskBody) Fault() *soap.Fault { return b.Err } func (b *queryVirtualDiskInfoTaskBody) Fault() *soap.Fault { return b.Err }
...@@ -62,7 +63,11 @@ func queryVirtualDiskInfoTask(ctx context.Context, r soap.RoundTripper, req *que ...@@ -62,7 +63,11 @@ func queryVirtualDiskInfoTask(ctx context.Context, r soap.RoundTripper, req *que
return nil, err return nil, err
} }
return resBody.Res, nil if resBody.Res != nil {
return resBody.Res, nil
}
return resBody.InternalRes, nil
} }
type VirtualDiskInfo struct { type VirtualDiskInfo struct {
......
...@@ -27,6 +27,18 @@ import ( ...@@ -27,6 +27,18 @@ import (
vim "github.com/vmware/govmomi/vim25/types" vim "github.com/vmware/govmomi/vim25/types"
) )
const (
Namespace = "pbm"
Path = "/pbm" + vim25.Path
)
var (
ServiceInstance = vim.ManagedObjectReference{
Type: "PbmServiceInstance",
Value: "ServiceInstance",
}
)
type Client struct { type Client struct {
*soap.Client *soap.Client
...@@ -34,13 +46,10 @@ type Client struct { ...@@ -34,13 +46,10 @@ type Client struct {
} }
func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) { func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
sc := c.Client.NewServiceClient("/pbm/sdk", "urn:pbm") sc := c.Client.NewServiceClient(Path, Namespace)
req := types.PbmRetrieveServiceContent{ req := types.PbmRetrieveServiceContent{
This: vim.ManagedObjectReference{ This: ServiceInstance,
Type: "PbmServiceInstance",
Value: "ServiceInstance",
},
} }
res, err := methods.PbmRetrieveServiceContent(ctx, sc, &req) res, err := methods.PbmRetrieveServiceContent(ctx, sc, &req)
......
/* /*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved. Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
......
/* /*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved. Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -152,6 +152,20 @@ func init() { ...@@ -152,6 +152,20 @@ func init() {
types.Add("pbm:PbmObjectType", reflect.TypeOf((*PbmObjectType)(nil)).Elem()) types.Add("pbm:PbmObjectType", reflect.TypeOf((*PbmObjectType)(nil)).Elem())
} }
type PbmOperation string
const (
PbmOperationCREATE = PbmOperation("CREATE")
PbmOperationREGISTER = PbmOperation("REGISTER")
PbmOperationRECONFIGURE = PbmOperation("RECONFIGURE")
PbmOperationMIGRATE = PbmOperation("MIGRATE")
PbmOperationCLONE = PbmOperation("CLONE")
)
func init() {
types.Add("pbm:PbmOperation", reflect.TypeOf((*PbmOperation)(nil)).Elem())
}
type PbmProfileCategoryEnum string type PbmProfileCategoryEnum string
const ( const (
...@@ -179,6 +193,7 @@ type PbmSystemCreatedProfileType string ...@@ -179,6 +193,7 @@ type PbmSystemCreatedProfileType string
const ( const (
PbmSystemCreatedProfileTypeVsanDefaultProfile = PbmSystemCreatedProfileType("VsanDefaultProfile") PbmSystemCreatedProfileTypeVsanDefaultProfile = PbmSystemCreatedProfileType("VsanDefaultProfile")
PbmSystemCreatedProfileTypeVVolDefaultProfile = PbmSystemCreatedProfileType("VVolDefaultProfile") PbmSystemCreatedProfileTypeVVolDefaultProfile = PbmSystemCreatedProfileType("VVolDefaultProfile")
PbmSystemCreatedProfileTypePmemDefaultProfile = PbmSystemCreatedProfileType("PmemDefaultProfile")
) )
func init() { func init() {
......
/* /*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved. Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
......
/* /*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved. Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -269,6 +269,12 @@ func init() { ...@@ -269,6 +269,12 @@ func init() {
types.Add("pbm:PbmAlreadyExists", reflect.TypeOf((*PbmAlreadyExists)(nil)).Elem()) types.Add("pbm:PbmAlreadyExists", reflect.TypeOf((*PbmAlreadyExists)(nil)).Elem())
} }
type PbmAlreadyExistsFault PbmAlreadyExists
func init() {
types.Add("pbm:PbmAlreadyExistsFault", reflect.TypeOf((*PbmAlreadyExistsFault)(nil)).Elem())
}
type PbmAssignDefaultRequirementProfile PbmAssignDefaultRequirementProfileRequestType type PbmAssignDefaultRequirementProfile PbmAssignDefaultRequirementProfileRequestType
func init() { func init() {
...@@ -1710,3 +1716,9 @@ type PbmVaioDataServiceInfo struct { ...@@ -1710,3 +1716,9 @@ type PbmVaioDataServiceInfo struct {
func init() { func init() {
types.Add("pbm:PbmVaioDataServiceInfo", reflect.TypeOf((*PbmVaioDataServiceInfo)(nil)).Elem()) types.Add("pbm:PbmVaioDataServiceInfo", reflect.TypeOf((*PbmVaioDataServiceInfo)(nil)).Elem())
} }
type VersionURI string
func init() {
types.Add("pbm:versionURI", reflect.TypeOf((*VersionURI)(nil)).Elem())
}
...@@ -122,7 +122,7 @@ func (f Filter) MatchPropertyList(props []types.DynamicProperty) bool { ...@@ -122,7 +122,7 @@ func (f Filter) MatchPropertyList(props []types.DynamicProperty) bool {
} }
} }
return true return len(f) == len(props) // false if a property such as VM "guest" is unset
} }
// MatchObjectContent returns a list of ObjectContent.Obj where the ObjectContent.PropSet matches the Filter. // MatchObjectContent returns a list of ObjectContent.Obj where the ObjectContent.PropSet matches the Filter.
......
...@@ -18,6 +18,7 @@ package session ...@@ -18,6 +18,7 @@ package session
import ( import (
"context" "context"
"net/http"
"net/url" "net/url"
"os" "os"
...@@ -89,14 +90,51 @@ func (sm *Manager) Login(ctx context.Context, u *url.Userinfo) error { ...@@ -89,14 +90,51 @@ func (sm *Manager) Login(ctx context.Context, u *url.Userinfo) error {
return nil return nil
} }
func (sm *Manager) LoginExtensionByCertificate(ctx context.Context, key string, locale string) error { // LoginExtensionByCertificate uses the vCenter SDK tunnel to login using a client certificate.
// The client certificate can be set using the soap.Client.SetCertificate method.
// See: https://kb.vmware.com/s/article/2004305
func (sm *Manager) LoginExtensionByCertificate(ctx context.Context, key string) error {
c := sm.client
u := c.URL()
if u.Hostname() != "sdkTunnel" {
sc := c.Tunnel()
c = &vim25.Client{
Client: sc,
RoundTripper: sc,
ServiceContent: c.ServiceContent,
}
// When http.Transport.Proxy is used, our thumbprint checker is bypassed, resulting in:
// "Post https://sdkTunnel:8089/sdk: x509: certificate is valid for $vcenter_hostname, not sdkTunnel"
// The only easy way around this is to disable verification for the call to LoginExtensionByCertificate().
// TODO: find a way to avoid disabling InsecureSkipVerify.
c.Transport.(*http.Transport).TLSClientConfig.InsecureSkipVerify = true
}
req := types.LoginExtensionByCertificate{ req := types.LoginExtensionByCertificate{
This: sm.Reference(), This: sm.Reference(),
ExtensionKey: key, ExtensionKey: key,
Locale: locale, Locale: Locale,
}
login, err := methods.LoginExtensionByCertificate(ctx, c, &req)
if err != nil {
return err
}
// Copy the session cookie
sm.client.Jar.SetCookies(u, c.Jar.Cookies(c.URL()))
sm.userSession = &login.Returnval
return nil
}
func (sm *Manager) LoginByToken(ctx context.Context) error {
req := types.LoginByToken{
This: sm.Reference(),
Locale: Locale,
} }
login, err := methods.LoginExtensionByCertificate(ctx, sm.client, &req) login, err := methods.LoginByToken(ctx, sm.client, &req)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -133,3 +133,28 @@ func (dc *Datacenter) PowerOnMultiVMTask(ctx *Context, req *types.PowerOnMultiVM ...@@ -133,3 +133,28 @@ func (dc *Datacenter) PowerOnMultiVMTask(ctx *Context, req *types.PowerOnMultiVM
}, },
} }
} }
func (d *Datacenter) DestroyTask(req *types.Destroy_Task) soap.HasFault {
task := CreateTask(d, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
folders := []types.ManagedObjectReference{
d.VmFolder,
d.HostFolder,
}
for _, ref := range folders {
if len(Map.Get(ref).(*Folder).ChildEntity) != 0 {
return nil, &types.ResourceInUse{}
}
}
Map.Get(*d.Parent).(*Folder).removeChild(d.Self)
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(),
},
}
}
...@@ -63,6 +63,14 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Ta ...@@ -63,6 +63,14 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Ta
VmVnicNetworkResourcePoolKey: spec.VmVnicNetworkResourcePoolKey, VmVnicNetworkResourcePoolKey: spec.VmVnicNetworkResourcePoolKey,
} }
if pg.Config.DefaultPortConfig == nil {
pg.Config.DefaultPortConfig = &types.VMwareDVSPortSetting{
Vlan: new(types.VmwareDistributedVirtualSwitchVlanIdSpec),
}
}
pg.PortKeys = []string{}
s.Portgroup = append(s.Portgroup, pg.Self) s.Portgroup = append(s.Portgroup, pg.Self)
s.Summary.PortgroupName = append(s.Summary.PortgroupName, pg.Name) s.Summary.PortgroupName = append(s.Summary.PortgroupName, pg.Name)
...@@ -111,10 +119,13 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_ ...@@ -111,10 +119,13 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
Map.AddReference(pg, &pg.Host, member.Host) Map.AddReference(pg, &pg.Host, member.Host)
} }
case types.ConfigSpecOperationRemove: case types.ConfigSpecOperationRemove:
if pg := FindReference(host.Network, s.Portgroup...); pg != nil { for _, ref := range host.Vm {
return nil, &types.ResourceInUse{ vm := Map.Get(ref).(*VirtualMachine)
Type: pg.Type, if pg := FindReference(vm.Network, s.Portgroup...); pg != nil {
Name: pg.Value, return nil, &types.ResourceInUse{
Type: pg.Type,
Name: pg.Value,
}
} }
} }
...@@ -170,10 +181,6 @@ func (s *DistributedVirtualSwitch) dvPortgroups(_ *types.DistributedVirtualSwitc ...@@ -170,10 +181,6 @@ func (s *DistributedVirtualSwitch) dvPortgroups(_ *types.DistributedVirtualSwitc
}, },
}) })
if pg.PortKeys == nil {
continue
}
for _, key := range pg.PortKeys { for _, key := range pg.PortKeys {
res = append(res, types.DistributedVirtualPort{ res = append(res, types.DistributedVirtualPort{
DvsUuid: s.Uuid, DvsUuid: s.Uuid,
......
...@@ -63,17 +63,14 @@ func NewEventManager(ref types.ManagedObjectReference) object.Reference { ...@@ -63,17 +63,14 @@ func NewEventManager(ref types.ManagedObjectReference) object.Reference {
} }
} }
func (m *EventManager) CreateCollectorForEvents(ctx *Context, req *types.CreateCollectorForEvents) soap.HasFault { func (m *EventManager) createCollector(ctx *Context, req *types.CreateCollectorForEvents) (*EventHistoryCollector, *soap.Fault) {
body := new(methods.CreateCollectorForEventsBody)
size, err := validatePageSize(req.Filter.MaxCount) size, err := validatePageSize(req.Filter.MaxCount)
if err != nil { if err != nil {
body.Fault_ = err return nil, err
return body
} }
if len(m.collectors) >= int(m.MaxCollector) { if len(m.collectors) >= int(m.MaxCollector) {
body.Fault_ = Fault("Too many event collectors to create", new(types.InvalidState)) return nil, Fault("Too many event collectors to create", new(types.InvalidState))
return body
} }
collector := &EventHistoryCollector{ collector := &EventHistoryCollector{
...@@ -83,6 +80,17 @@ func (m *EventManager) CreateCollectorForEvents(ctx *Context, req *types.CreateC ...@@ -83,6 +80,17 @@ func (m *EventManager) CreateCollectorForEvents(ctx *Context, req *types.CreateC
collector.Filter = req.Filter collector.Filter = req.Filter
collector.fillPage(size) collector.fillPage(size)
return collector, nil
}
func (m *EventManager) CreateCollectorForEvents(ctx *Context, req *types.CreateCollectorForEvents) soap.HasFault {
body := new(methods.CreateCollectorForEventsBody)
collector, err := m.createCollector(ctx, req)
if err != nil {
body.Fault_ = err
return body
}
ref := ctx.Session.Put(collector).Reference() ref := ctx.Session.Put(collector).Reference()
m.collectors[ref] = collector m.collectors[ref] = collector
...@@ -93,6 +101,27 @@ func (m *EventManager) CreateCollectorForEvents(ctx *Context, req *types.CreateC ...@@ -93,6 +101,27 @@ func (m *EventManager) CreateCollectorForEvents(ctx *Context, req *types.CreateC
return body return body
} }
func (m *EventManager) QueryEvents(ctx *Context, req *types.QueryEvents) soap.HasFault {
if Map.IsESX() {
return &methods.QueryEventsBody{
Fault_: Fault("", new(types.NotImplemented)),
}
}
body := new(methods.QueryEventsBody)
collector, err := m.createCollector(ctx, &types.CreateCollectorForEvents{Filter: req.Filter})
if err != nil {
body.Fault_ = err
return body
}
body.Res = &types.QueryEventsResponse{
Returnval: collector.GetLatestPage(),
}
return body
}
// formatMessage applies the EventDescriptionEventDetail.FullFormat template to the given event's FullFormattedMessage field. // formatMessage applies the EventDescriptionEventDetail.FullFormat template to the given event's FullFormattedMessage field.
func (m *EventManager) formatMessage(event types.BaseEvent) { func (m *EventManager) formatMessage(event types.BaseEvent) {
id := reflect.ValueOf(event).Elem().Type().Name() id := reflect.ValueOf(event).Elem().Type().Name()
...@@ -372,15 +401,23 @@ func (c *EventHistoryCollector) DestroyCollector(ctx *Context, req *types.Destro ...@@ -372,15 +401,23 @@ func (c *EventHistoryCollector) DestroyCollector(ctx *Context, req *types.Destro
} }
} }
func (c *EventHistoryCollector) Get() mo.Reference { func (c *EventHistoryCollector) GetLatestPage() []types.BaseEvent {
clone := *c var latestPage []types.BaseEvent
c.page.Do(func(val interface{}) { c.page.Do(func(val interface{}) {
if val == nil { if val == nil {
return return
} }
clone.LatestPage = append(clone.LatestPage, val.(types.BaseEvent)) latestPage = append(latestPage, val.(types.BaseEvent))
}) })
return latestPage
}
func (c *EventHistoryCollector) Get() mo.Reference {
clone := *c
clone.LatestPage = clone.GetLatestPage()
return &clone return &clone
} }
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"path" "path"
"strings"
"github.com/google/uuid" "github.com/google/uuid"
...@@ -514,6 +515,17 @@ func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault { ...@@ -514,6 +515,17 @@ func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault {
} }
} }
dvs.AddDVPortgroupTask(&types.AddDVPortgroup_Task{
Spec: []types.DVPortgroupConfigSpec{{
Name: dvs.Name + "-DVUplinks" + strings.TrimPrefix(dvs.Self.Value, "dvs"),
DefaultPortConfig: &types.VMwareDVSPortSetting{
Vlan: &types.VmwareDistributedVirtualSwitchTrunkVlanSpec{
VlanId: []types.NumericRange{{Start: 0, End: 4094}},
},
},
}},
})
return dvs.Reference(), nil return dvs.Reference(), nil
}) })
...@@ -527,3 +539,45 @@ func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault { ...@@ -527,3 +539,45 @@ func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault {
func (f *Folder) RenameTask(r *types.Rename_Task) soap.HasFault { func (f *Folder) RenameTask(r *types.Rename_Task) soap.HasFault {
return RenameTask(f, r) return RenameTask(f, r)
} }
func (f *Folder) DestroyTask(req *types.Destroy_Task) soap.HasFault {
type destroyer interface {
mo.Reference
DestroyTask(*types.Destroy_Task) soap.HasFault
}
task := CreateTask(f, "destroy", func(*Task) (types.AnyType, types.BaseMethodFault) {
// Attempt to destroy all children
for _, c := range f.ChildEntity {
obj, ok := Map.Get(c).(destroyer)
if !ok {
continue
}
var fault types.BaseMethodFault
Map.WithLock(obj, func() {
id := obj.DestroyTask(&types.Destroy_Task{
This: c,
}).(*methods.Destroy_TaskBody).Res.Returnval
t := Map.Get(id).(*Task)
if t.Info.Error != nil {
fault = t.Info.Error.Fault // For example, can't destroy a powered on VM
}
})
if fault != nil {
return nil, fault
}
}
// Remove the folder itself
Map.Get(*f.Parent).(*Folder).removeChild(f.Self)
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(),
},
}
}
...@@ -173,6 +173,25 @@ func CreateStandaloneHost(f *Folder, spec types.HostConnectSpec) (*HostSystem, t ...@@ -173,6 +173,25 @@ func CreateStandaloneHost(f *Folder, spec types.HostConnectSpec) (*HostSystem, t
return host, nil return host, nil
} }
func (h *HostSystem) DestroyTask(req *types.Destroy_Task) soap.HasFault {
task := CreateTask(h, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
if len(h.Vm) > 0 {
return nil, &types.ResourceInUse{}
}
f := Map.getEntityParent(h, "Folder").(*Folder)
f.removeChild(h.Reference())
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(),
},
}
}
func (h *HostSystem) EnterMaintenanceModeTask(spec *types.EnterMaintenanceMode_Task) soap.HasFault { func (h *HostSystem) EnterMaintenanceModeTask(spec *types.EnterMaintenanceMode_Task) soap.HasFault {
task := CreateTask(h, "enterMaintenanceMode", func(t *Task) (types.AnyType, types.BaseMethodFault) { task := CreateTask(h, "enterMaintenanceMode", func(t *Task) (types.AnyType, types.BaseMethodFault) {
h.Runtime.InMaintenanceMode = true h.Runtime.InMaintenanceMode = true
......
...@@ -111,17 +111,29 @@ func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{} ...@@ -111,17 +111,29 @@ func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{}
pval = &types.ArrayOfString{ pval = &types.ArrayOfString{
String: v, String: v,
} }
case []uint8:
pval = &types.ArrayOfByte{
Byte: v,
}
case []int16:
pval = &types.ArrayOfShort{
Short: v,
}
case []int32: case []int32:
pval = &types.ArrayOfInt{ pval = &types.ArrayOfInt{
Int: v, Int: v,
} }
case []int64:
pval = &types.ArrayOfLong{
Long: v,
}
default: default:
kind := f.Type.Elem().Name() kind := f.Type.Elem().Name()
// Remove govmomi interface prefix name // Remove govmomi interface prefix name
if strings.HasPrefix(kind, "Base") { if strings.HasPrefix(kind, "Base") {
kind = kind[4:] kind = kind[4:]
} }
akind, _ := typeFunc("ArrayOf" + kind) akind, _ := defaultMapType("ArrayOf" + kind)
a := reflect.New(akind) a := reflect.New(akind)
a.Elem().FieldByName(kind).Set(rval) a.Elem().FieldByName(kind).Set(rval)
pval = a.Interface() pval = a.Interface()
...@@ -278,14 +290,10 @@ func (rr *retrieveResult) collectFields(ctx *Context, rval reflect.Value, fields ...@@ -278,14 +290,10 @@ func (rr *retrieveResult) collectFields(ctx *Context, rval reflect.Value, fields
seen[name] = true seen[name] = true
val, err := fieldValue(rval, name) val, err := fieldValue(rval, name)
if err == nil {
rr.add(ctx, name, val, content)
continue
}
switch err { switch err {
case errEmptyField: case nil, errEmptyField:
// ok rr.add(ctx, name, val, content)
case errMissingField: case errMissingField:
content.MissingSet = append(content.MissingSet, types.MissingProperty{ content.MissingSet = append(content.MissingSet, types.MissingProperty{
Path: name, Path: name,
...@@ -469,6 +477,19 @@ func (pc *PropertyCollector) RetrievePropertiesEx(ctx *Context, r *types.Retriev ...@@ -469,6 +477,19 @@ func (pc *PropertyCollector) RetrievePropertiesEx(ctx *Context, r *types.Retriev
if fault != nil { if fault != nil {
body.Fault_ = Fault("", fault) body.Fault_ = Fault("", fault)
} else { } else {
objects := res.Objects[:0]
for _, o := range res.Objects {
propSet := o.PropSet[:0]
for _, p := range o.PropSet {
if p.Val != nil {
propSet = append(propSet, p)
}
}
o.PropSet = propSet
objects = append(objects, o)
}
res.Objects = objects
body.Res = &types.RetrievePropertiesExResponse{ body.Res = &types.RetrievePropertiesExResponse{
Returnval: res, Returnval: res,
} }
......
...@@ -24,7 +24,7 @@ import ( ...@@ -24,7 +24,7 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types" "github.com/vmware/govmomi/vim25/types"
) )
...@@ -40,6 +40,7 @@ var refValueMap = map[string]string{ ...@@ -40,6 +40,7 @@ var refValueMap = map[string]string{
"VirtualMachine": "vm", "VirtualMachine": "vm",
"VirtualMachineSnapshot": "snapshot", "VirtualMachineSnapshot": "snapshot",
"VmwareDistributedVirtualSwitch": "dvs", "VmwareDistributedVirtualSwitch": "dvs",
"DistributedVirtualSwitch": "dvs",
} }
// Map is the default Registry instance. // Map is the default Registry instance.
...@@ -59,6 +60,9 @@ type Registry struct { ...@@ -59,6 +60,9 @@ type Registry struct {
handlers map[types.ManagedObjectReference]RegisterObject handlers map[types.ManagedObjectReference]RegisterObject
locks map[types.ManagedObjectReference]sync.Locker locks map[types.ManagedObjectReference]sync.Locker
counter int counter int
Namespace string
Path string
} }
// NewRegistry creates a new instances of Registry // NewRegistry creates a new instances of Registry
...@@ -67,11 +71,23 @@ func NewRegistry() *Registry { ...@@ -67,11 +71,23 @@ func NewRegistry() *Registry {
objects: make(map[types.ManagedObjectReference]mo.Reference), objects: make(map[types.ManagedObjectReference]mo.Reference),
handlers: make(map[types.ManagedObjectReference]RegisterObject), handlers: make(map[types.ManagedObjectReference]RegisterObject),
locks: make(map[types.ManagedObjectReference]sync.Locker), locks: make(map[types.ManagedObjectReference]sync.Locker),
Namespace: vim25.Namespace,
Path: vim25.Path,
} }
return r return r
} }
func (r *Registry) typeFunc(name string) (reflect.Type, bool) {
if r.Namespace != "" && r.Namespace != vim25.Namespace {
if kind, ok := defaultMapType(r.Namespace + ":" + name); ok {
return kind, ok
}
}
return defaultMapType(name)
}
// typeName returns the type of the given object. // typeName returns the type of the given object.
func typeName(item mo.Reference) string { func typeName(item mo.Reference) string {
return reflect.TypeOf(item).Elem().Name() return reflect.TypeOf(item).Elem().Name()
...@@ -332,7 +348,7 @@ func (r *Registry) removeString(obj mo.Reference, field *[]string, val string) { ...@@ -332,7 +348,7 @@ func (r *Registry) removeString(obj mo.Reference, field *[]string, val string) {
} }
func (r *Registry) content() types.ServiceContent { func (r *Registry) content() types.ServiceContent {
return r.Get(methods.ServiceInstance).(*ServiceInstance).Content return r.Get(vim25.ServiceInstance).(*ServiceInstance).Content
} }
// IsESX returns true if this Registry maps an ESX model // IsESX returns true if this Registry maps an ESX model
...@@ -380,6 +396,11 @@ func (r *Registry) SessionManager() *SessionManager { ...@@ -380,6 +396,11 @@ func (r *Registry) SessionManager() *SessionManager {
return r.Get(r.content().SessionManager.Reference()).(*SessionManager) return r.Get(r.content().SessionManager.Reference()).(*SessionManager)
} }
// OptionManager returns the OptionManager singleton
func (r *Registry) OptionManager() *OptionManager {
return r.Get(r.content().Setting.Reference()).(*OptionManager)
}
func (r *Registry) MarshalJSON() ([]byte, error) { func (r *Registry) MarshalJSON() ([]byte, error) {
r.m.Lock() r.m.Lock()
defer r.m.Unlock() defer r.m.Unlock()
......
...@@ -19,8 +19,10 @@ package simulator ...@@ -19,8 +19,10 @@ package simulator
import ( import (
"time" "time"
"github.com/google/uuid"
"github.com/vmware/govmomi/object" "github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/vpx" "github.com/vmware/govmomi/simulator/vpx"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap" "github.com/vmware/govmomi/vim25/soap"
...@@ -36,7 +38,7 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service ...@@ -36,7 +38,7 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service
s := &ServiceInstance{} s := &ServiceInstance{}
s.Self = methods.ServiceInstance s.Self = vim25.ServiceInstance
s.Content = content s.Content = content
Map.Put(s) Map.Put(s)
...@@ -49,6 +51,7 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service ...@@ -49,6 +51,7 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service
if content.About.ApiType == "HostAgent" { if content.About.ApiType == "HostAgent" {
CreateDefaultESX(f) CreateDefaultESX(f)
} else { } else {
content.About.InstanceUuid = uuid.New().String()
setting = vpx.Setting setting = vpx.Setting
} }
......
...@@ -49,33 +49,85 @@ func NewSessionManager(ref types.ManagedObjectReference) object.Reference { ...@@ -49,33 +49,85 @@ func NewSessionManager(ref types.ManagedObjectReference) object.Reference {
return s return s
} }
func (s *SessionManager) Login(ctx *Context, login *types.Login) soap.HasFault { func createSession(ctx *Context, name string, locale string) types.UserSession {
body := &methods.LoginBody{} now := time.Now().UTC()
if login.Locale == "" { if locale == "" {
login.Locale = session.Locale locale = session.Locale
} }
if login.UserName == "" || login.Password == "" || ctx.Session != nil { session := Session{
UserSession: types.UserSession{
Key: uuid.New().String(),
UserName: name,
FullName: name,
LoginTime: now,
LastActiveTime: now,
Locale: locale,
MessageLocale: locale,
},
Registry: NewRegistry(),
}
ctx.SetSession(session, true)
return session.UserSession
}
func (s *SessionManager) Login(ctx *Context, req *types.Login) soap.HasFault {
body := new(methods.LoginBody)
if req.UserName == "" || req.Password == "" || ctx.Session != nil {
body.Fault_ = invalidLogin body.Fault_ = invalidLogin
} else { } else {
session := Session{ body.Res = &types.LoginResponse{
UserSession: types.UserSession{ Returnval: createSession(ctx, req.UserName, req.Locale),
Key: uuid.New().String(),
UserName: login.UserName,
FullName: login.UserName,
LoginTime: time.Now(),
LastActiveTime: time.Now(),
Locale: login.Locale,
MessageLocale: login.Locale,
},
Registry: NewRegistry(),
} }
}
ctx.SetSession(session, true) return body
}
body.Res = &types.LoginResponse{ func (s *SessionManager) LoginExtensionByCertificate(ctx *Context, req *types.LoginExtensionByCertificate) soap.HasFault {
Returnval: session.UserSession, body := new(methods.LoginExtensionByCertificateBody)
if ctx.req.TLS == nil || len(ctx.req.TLS.PeerCertificates) == 0 {
body.Fault_ = Fault("", new(types.NoClientCertificate))
return body
}
if req.ExtensionKey == "" || ctx.Session != nil {
body.Fault_ = invalidLogin
} else {
body.Res = &types.LoginExtensionByCertificateResponse{
Returnval: createSession(ctx, req.ExtensionKey, req.Locale),
}
}
return body
}
func (s *SessionManager) LoginByToken(ctx *Context, req *types.LoginByToken) soap.HasFault {
body := new(methods.LoginByTokenBody)
if ctx.Session != nil {
body.Fault_ = invalidLogin
} else {
var subject struct {
ID string `xml:"Assertion>Subject>NameID"`
}
if s, ok := ctx.Header.Security.(*Element); ok {
_ = s.Decode(&subject)
}
if subject.ID == "" {
body.Fault_ = invalidLogin
return body
}
body.Res = &types.LoginByTokenResponse{
Returnval: createSession(ctx, subject.ID, req.Locale),
} }
} }
...@@ -163,6 +215,7 @@ var internalContext = &Context{ ...@@ -163,6 +215,7 @@ var internalContext = &Context{
}, },
Registry: NewRegistry(), Registry: NewRegistry(),
}, },
Map: Map,
} }
var invalidLogin = Fault("Login failure", new(types.InvalidLogin)) var invalidLogin = Fault("Login failure", new(types.InvalidLogin))
...@@ -175,7 +228,9 @@ type Context struct { ...@@ -175,7 +228,9 @@ type Context struct {
context.Context context.Context
Session *Session Session *Session
Header soap.Header
Caller *types.ManagedObjectReference Caller *types.ManagedObjectReference
Map *Registry
} }
// mapSession maps an HTTP cookie to a Session. // mapSession maps an HTTP cookie to a Session.
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/base64"
"encoding/json" "encoding/json"
"encoding/pem" "encoding/pem"
"flag" "flag"
...@@ -28,6 +29,7 @@ import ( ...@@ -28,6 +29,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
...@@ -35,6 +37,7 @@ import ( ...@@ -35,6 +37,7 @@ import (
"path" "path"
"reflect" "reflect"
"sort" "sort"
"strconv"
"strings" "strings"
"github.com/vmware/govmomi/find" "github.com/vmware/govmomi/find"
...@@ -51,15 +54,17 @@ var Trace = false ...@@ -51,15 +54,17 @@ var Trace = false
// Method encapsulates a decoded SOAP client request // Method encapsulates a decoded SOAP client request
type Method struct { type Method struct {
Name string Name string
This types.ManagedObjectReference This types.ManagedObjectReference
Body types.AnyType Header soap.Header
Body types.AnyType
} }
// Service decodes incoming requests and dispatches to a Handler // Service decodes incoming requests and dispatches to a Handler
type Service struct { type Service struct {
client *vim25.Client client *vim25.Client
sm *SessionManager sm *SessionManager
sdk map[string]*Registry
readAll func(io.Reader) ([]byte, error) readAll func(io.Reader) ([]byte, error)
...@@ -70,7 +75,8 @@ type Service struct { ...@@ -70,7 +75,8 @@ type Service struct {
// Server provides a simulator Service over HTTP // Server provides a simulator Service over HTTP
type Server struct { type Server struct {
*httptest.Server *httptest.Server
URL *url.URL URL *url.URL
Tunnel int
caFile string caFile string
} }
...@@ -80,6 +86,7 @@ func New(instance *ServiceInstance) *Service { ...@@ -80,6 +86,7 @@ func New(instance *ServiceInstance) *Service {
s := &Service{ s := &Service{
readAll: ioutil.ReadAll, readAll: ioutil.ReadAll,
sm: Map.SessionManager(), sm: Map.SessionManager(),
sdk: make(map[string]*Registry),
} }
s.client, _ = vim25.NewClient(context.Background(), s) s.client, _ = vim25.NewClient(context.Background(), s)
...@@ -110,12 +117,12 @@ func Fault(msg string, fault types.BaseMethodFault) *soap.Fault { ...@@ -110,12 +117,12 @@ func Fault(msg string, fault types.BaseMethodFault) *soap.Fault {
} }
func (s *Service) call(ctx *Context, method *Method) soap.HasFault { func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
handler := Map.Get(method.This) handler := ctx.Map.Get(method.This)
session := ctx.Session session := ctx.Session
if session == nil { if session == nil {
switch method.Name { switch method.Name {
case "RetrieveServiceContent", "Login", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession": case "RetrieveServiceContent", "List", "Login", "LoginByToken", "LoginExtensionByCertificate", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
// ok for now, TODO: authz // ok for now, TODO: authz
default: default:
fault := &types.NotAuthenticated{ fault := &types.NotAuthenticated{
...@@ -170,7 +177,7 @@ func (s *Service) call(ctx *Context, method *Method) soap.HasFault { ...@@ -170,7 +177,7 @@ func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
args = append(args, reflect.ValueOf(ctx)) args = append(args, reflect.ValueOf(ctx))
} }
args = append(args, reflect.ValueOf(method.Body)) args = append(args, reflect.ValueOf(method.Body))
Map.WithLock(handler, func() { ctx.Map.WithLock(handler, func() {
res = m.Call(args) res = m.Call(args)
}) })
...@@ -197,6 +204,7 @@ func (s *Service) RoundTrip(ctx context.Context, request, response soap.HasFault ...@@ -197,6 +204,7 @@ func (s *Service) RoundTrip(ctx context.Context, request, response soap.HasFault
} }
res := s.call(&Context{ res := s.call(&Context{
Map: Map,
Context: ctx, Context: ctx,
Session: internalContext.Session, Session: internalContext.Session,
}, method) }, method)
...@@ -281,6 +289,16 @@ func (s *Service) About(w http.ResponseWriter, r *http.Request) { ...@@ -281,6 +289,16 @@ func (s *Service) About(w http.ResponseWriter, r *http.Request) {
_ = enc.Encode(&about) _ = enc.Encode(&about)
} }
// RegisterSDK adds an HTTP handler for the Registry's Path and Namespace.
func (s *Service) RegisterSDK(r *Registry) {
if s.ServeMux == nil {
s.ServeMux = http.NewServeMux()
}
s.sdk[r.Path] = r
s.ServeMux.HandleFunc(r.Path, s.ServeSDK)
}
// ServeSDK implements the http.Handler interface // ServeSDK implements the http.Handler interface
func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) { func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" { if r.Method != "POST" {
...@@ -305,17 +323,19 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) { ...@@ -305,17 +323,19 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
res: w, res: w,
m: s.sm, m: s.sm,
Map: s.sdk[r.URL.Path],
Context: context.Background(), Context: context.Background(),
} }
Map.WithLock(s.sm, ctx.mapSession) ctx.Map.WithLock(s.sm, ctx.mapSession)
var res soap.HasFault var res soap.HasFault
var soapBody interface{} var soapBody interface{}
method, err := UnmarshalBody(body) method, err := UnmarshalBody(ctx.Map.typeFunc, body)
if err != nil { if err != nil {
res = serverFault(err.Error()) res = serverFault(err.Error())
} else { } else {
ctx.Header = method.Header
res = s.call(ctx, method) res = s.call(ctx, method)
} }
...@@ -447,15 +467,10 @@ func (*Service) ServiceVersions(w http.ResponseWriter, r *http.Request) { ...@@ -447,15 +467,10 @@ func (*Service) ServiceVersions(w http.ResponseWriter, r *http.Request) {
// NewServer returns an http Server instance for the given service // NewServer returns an http Server instance for the given service
func (s *Service) NewServer() *Server { func (s *Service) NewServer() *Server {
mux := s.ServeMux s.RegisterSDK(Map)
if mux == nil {
mux = http.NewServeMux()
}
path := "/sdk"
mux.HandleFunc(path, s.ServeSDK) mux := s.ServeMux
mux.HandleFunc(path+"/vimServiceVersions.xml", s.ServiceVersions) mux.HandleFunc(Map.Path+"/vimServiceVersions.xml", s.ServiceVersions)
mux.HandleFunc(folderPrefix, s.ServeDatastore) mux.HandleFunc(folderPrefix, s.ServeDatastore)
mux.HandleFunc("/about", s.About) mux.HandleFunc("/about", s.About)
...@@ -466,7 +481,7 @@ func (s *Service) NewServer() *Server { ...@@ -466,7 +481,7 @@ func (s *Service) NewServer() *Server {
u := &url.URL{ u := &url.URL{
Scheme: "http", Scheme: "http",
Host: ts.Listener.Addr().String(), Host: ts.Listener.Addr().String(),
Path: path, Path: Map.Path,
User: url.UserPassword("user", "pass"), User: url.UserPassword("user", "pass"),
} }
...@@ -478,14 +493,31 @@ func (s *Service) NewServer() *Server { ...@@ -478,14 +493,31 @@ func (s *Service) NewServer() *Server {
_ = f.Value.Set("") _ = f.Value.Set("")
} }
cert := ""
if s.TLS == nil { if s.TLS == nil {
ts.Start() ts.Start()
} else { } else {
ts.TLS = s.TLS ts.TLS = s.TLS
ts.TLS.ClientAuth = tls.RequestClientCert // Used by SessionManager.LoginExtensionByCertificate
ts.StartTLS() ts.StartTLS()
u.Scheme += "s" u.Scheme += "s"
cert = base64.StdEncoding.EncodeToString(ts.TLS.Certificates[0].Certificate[0])
} }
// Add vcsim config to OptionManager for use by SDK handlers (see lookup/simulator for example)
m := Map.OptionManager()
m.Setting = append(m.Setting,
&types.OptionValue{
Key: "vcsim.server.url",
Value: ts.URL,
},
&types.OptionValue{
Key: "vcsim.server.cert",
Value: cert,
},
)
return &Server{ return &Server{
Server: ts, Server: ts,
URL: u, URL: u,
...@@ -525,6 +557,60 @@ func (s *Server) CertificateFile() (string, error) { ...@@ -525,6 +557,60 @@ func (s *Server) CertificateFile() (string, error) {
return s.caFile, pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) return s.caFile, pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
} }
// proxy tunnels SDK requests
func (s *Server) proxy(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodConnect {
http.Error(w, "", http.StatusMethodNotAllowed)
return
}
dst, err := net.Dial("tcp", s.URL.Host)
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
w.WriteHeader(http.StatusOK)
src, _, err := w.(http.Hijacker).Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
go io.Copy(src, dst)
go func() {
_, _ = io.Copy(dst, src)
_ = dst.Close()
_ = src.Close()
}()
}
// StartTunnel runs an HTTP proxy for tunneling SDK requests that require TLS client certificate authentication.
func (s *Server) StartTunnel() error {
tunnel := &http.Server{
Addr: fmt.Sprintf("%s:%d", s.URL.Hostname(), s.Tunnel),
Handler: http.HandlerFunc(s.proxy),
}
l, err := net.Listen("tcp", tunnel.Addr)
if err != nil {
return err
}
if s.Tunnel == 0 {
s.Tunnel = l.Addr().(*net.TCPAddr).Port
}
// Set client proxy port (defaults to vCenter host port 80 in real life)
q := s.URL.Query()
q.Set("GOVMOMI_TUNNEL_PROXY_PORT", strconv.Itoa(s.Tunnel))
s.URL.RawQuery = q.Encode()
go tunnel.Serve(l)
return nil
}
// Close shuts down the server and blocks until all outstanding // Close shuts down the server and blocks until all outstanding
// requests on this server have completed. // requests on this server have completed.
func (s *Server) Close() { func (s *Server) Close() {
...@@ -536,7 +622,6 @@ func (s *Server) Close() { ...@@ -536,7 +622,6 @@ func (s *Server) Close() {
var ( var (
vim25MapType = types.TypeFunc() vim25MapType = types.TypeFunc()
typeFunc = defaultMapType
) )
func defaultMapType(name string) (reflect.Type, bool) { func defaultMapType(name string) (reflect.Type, bool) {
...@@ -552,14 +637,39 @@ func defaultMapType(name string) (reflect.Type, bool) { ...@@ -552,14 +637,39 @@ func defaultMapType(name string) (reflect.Type, bool) {
return typ, ok return typ, ok
} }
// UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type // Element can be used to defer decoding of an XML node.
func UnmarshalBody(data []byte) (*Method, error) { type Element struct {
body := struct { start xml.StartElement
inner struct {
Content string `xml:",innerxml"` Content string `xml:",innerxml"`
}{} }
typeFunc func(string) (reflect.Type, bool)
}
func (e *Element) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
e.start = start
return d.DecodeElement(&e.inner, &start)
}
func (e *Element) decoder() *xml.Decoder {
decoder := xml.NewDecoder(strings.NewReader(e.inner.Content))
decoder.TypeFunc = e.typeFunc // required to decode interface types
return decoder
}
func (e *Element) Decode(val interface{}) error {
return e.decoder().DecodeElement(val, &e.start)
}
// UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type
func UnmarshalBody(typeFunc func(string) (reflect.Type, bool), data []byte) (*Method, error) {
body := &Element{typeFunc: typeFunc}
req := soap.Envelope{ req := soap.Envelope{
Body: &body, Header: &soap.Header{
Security: new(Element),
},
Body: body,
} }
err := xml.Unmarshal(data, &req) err := xml.Unmarshal(data, &req)
...@@ -567,40 +677,38 @@ func UnmarshalBody(data []byte) (*Method, error) { ...@@ -567,40 +677,38 @@ func UnmarshalBody(data []byte) (*Method, error) {
return nil, fmt.Errorf("xml.Unmarshal: %s", err) return nil, fmt.Errorf("xml.Unmarshal: %s", err)
} }
decoder := xml.NewDecoder(bytes.NewReader([]byte(body.Content))) var start xml.StartElement
decoder.TypeFunc = typeFunc // required to decode interface types var ok bool
decoder := body.decoder()
var start *xml.StartElement
for { for {
tok, derr := decoder.Token() tok, derr := decoder.Token()
if derr != nil { if derr != nil {
return nil, fmt.Errorf("decoding body: %s", err) return nil, fmt.Errorf("decoding: %s", derr)
} }
if t, ok := tok.(xml.StartElement); ok { if start, ok = tok.(xml.StartElement); ok {
start = &t
break break
} }
} }
kind := start.Name.Local if !ok {
return nil, fmt.Errorf("decoding: method token not found")
}
kind := start.Name.Local
rtype, ok := typeFunc(kind) rtype, ok := typeFunc(kind)
if !ok { if !ok {
return nil, fmt.Errorf("no vmomi type defined for '%s'", kind) return nil, fmt.Errorf("no vmomi type defined for '%s'", kind)
} }
var val interface{} val := reflect.New(rtype).Interface()
if rtype != nil {
val = reflect.New(rtype).Interface()
}
err = decoder.DecodeElement(val, start) err = decoder.DecodeElement(val, &start)
if err != nil { if err != nil {
return nil, fmt.Errorf("decoding %s: %s", kind, err) return nil, fmt.Errorf("decoding %s: %s", kind, err)
} }
method := &Method{Name: kind, Body: val} method := &Method{Name: kind, Header: *req.Header, Body: val}
field := reflect.ValueOf(val).Elem().FieldByName("This") field := reflect.ValueOf(val).Elem().FieldByName("This")
......
...@@ -39,6 +39,10 @@ func (v *VirtualMachineSnapshot) RemoveSnapshotTask(req *types.RemoveSnapshot_Ta ...@@ -39,6 +39,10 @@ func (v *VirtualMachineSnapshot) RemoveSnapshotTask(req *types.RemoveSnapshot_Ta
} }
vm.Snapshot.RootSnapshotList = removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren) vm.Snapshot.RootSnapshotList = removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren)
if len(vm.Snapshot.RootSnapshotList) == 0 {
vm.Snapshot = nil
}
}) })
return nil, nil return nil, nil
......
...@@ -17,11 +17,13 @@ limitations under the License. ...@@ -17,11 +17,13 @@ limitations under the License.
package simulator package simulator
import ( import (
"bytes"
"fmt" "fmt"
"log" "log"
"net" "net"
"os" "os"
"path" "path"
"strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
"time" "time"
...@@ -63,7 +65,7 @@ func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualM ...@@ -63,7 +65,7 @@ func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualM
MemoryAllocation: &rspec.MemoryAllocation, MemoryAllocation: &rspec.MemoryAllocation,
CpuAllocation: &rspec.CpuAllocation, CpuAllocation: &rspec.CpuAllocation,
} }
vm.Snapshot = &types.VirtualMachineSnapshotInfo{} vm.Snapshot = nil // intentionally set to nil until a snapshot is created
vm.Storage = &types.VirtualMachineStorageInfo{ vm.Storage = &types.VirtualMachineStorageInfo{
Timestamp: time.Now(), Timestamp: time.Now(),
} }
...@@ -420,6 +422,28 @@ func (vm *VirtualMachine) generateMAC() string { ...@@ -420,6 +422,28 @@ func (vm *VirtualMachine) generateMAC() string {
return mac.String() return mac.String()
} }
func numberToString(n int64, sep rune) string {
buf := &bytes.Buffer{}
if n < 0 {
n = -n
buf.WriteRune('-')
}
s := strconv.FormatInt(n, 10)
pos := 3 - (len(s) % 3)
for i := 0; i < len(s); i++ {
if pos == 3 {
if i != 0 {
buf.WriteRune(sep)
}
pos = 0
}
pos++
buf.WriteByte(s[i])
}
return buf.String()
}
func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec *types.VirtualDeviceConfigSpec) types.BaseMethodFault { func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec *types.VirtualDeviceConfigSpec) types.BaseMethodFault {
device := spec.Device device := spec.Device
d := device.GetVirtualDevice() d := device.GetVirtualDevice()
...@@ -469,6 +493,7 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec ...@@ -469,6 +493,7 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec
c.MacAddress = vm.generateMAC() c.MacAddress = vm.generateMAC()
} }
case *types.VirtualDisk: case *types.VirtualDisk:
summary = fmt.Sprintf("%s KB", numberToString(x.CapacityInKB, ','))
switch b := d.Backing.(type) { switch b := d.Backing.(type) {
case types.BaseVirtualDeviceFileBackingInfo: case types.BaseVirtualDeviceFileBackingInfo:
info := b.GetVirtualDeviceFileBackingInfo() info := b.GetVirtualDeviceFileBackingInfo()
...@@ -991,8 +1016,7 @@ func (vm *VirtualMachine) RemoveAllSnapshotsTask(req *types.RemoveAllSnapshots_T ...@@ -991,8 +1016,7 @@ func (vm *VirtualMachine) RemoveAllSnapshotsTask(req *types.RemoveAllSnapshots_T
refs := allSnapshotsInTree(vm.Snapshot.RootSnapshotList) refs := allSnapshotsInTree(vm.Snapshot.RootSnapshotList)
vm.Snapshot.CurrentSnapshot = nil vm.Snapshot = nil
vm.Snapshot.RootSnapshotList = nil
for _, ref := range refs { for _, ref := range refs {
Map.Remove(ref) Map.Remove(ref)
......
...@@ -58,6 +58,10 @@ var Setting = []types.BaseOptionValue{ ...@@ -58,6 +58,10 @@ var Setting = []types.BaseOptionValue{
Value: "https://127.0.0.1/sso-adminserver/sdk/vsphere.local", Value: "https://127.0.0.1/sso-adminserver/sdk/vsphere.local",
}, },
&types.OptionValue{ &types.OptionValue{
Key: "VirtualCenter.InstanceName",
Value: "127.0.0.1",
},
&types.OptionValue{
Key: "event.batchsize", Key: "event.batchsize",
Value: int32(2000), Value: int32(2000),
}, },
......
/*
Copyright (c) 2018 VMware, Inc. All 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 sts
import (
"context"
"crypto/tls"
"errors"
"net/url"
"time"
"github.com/vmware/govmomi/lookup"
"github.com/vmware/govmomi/lookup/types"
"github.com/vmware/govmomi/sts/internal"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/soap"
)
const (
Namespace = "oasis:names:tc:SAML:2.0:assertion"
Path = "/sts/STSService"
)
// Client is a soap.Client targeting the STS (Secure Token Service) API endpoint.
type Client struct {
*soap.Client
}
// NewClient returns a client targeting the STS API endpoint.
// The Client.URL will be set to that of the Lookup Service's endpoint registration,
// as the SSO endpoint can be external to vCenter. If the Lookup Service is not available,
// URL defaults to Path on the vim25.Client.URL.Host.
func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
filter := &types.LookupServiceRegistrationFilter{
ServiceType: &types.LookupServiceRegistrationServiceType{
Product: "com.vmware.cis",
Type: "sso:sts",
},
EndpointType: &types.LookupServiceRegistrationEndpointType{
Protocol: "wsTrust",
Type: "com.vmware.cis.cs.identity.sso",
},
}
url := lookup.EndpointURL(ctx, c, Path, filter)
sc := c.Client.NewServiceClient(url, Namespace)
return &Client{sc}, nil
}
// TokenRequest parameters for issuing a SAML token.
// At least one of Userinfo or Certificate must be specified.
type TokenRequest struct {
Userinfo *url.Userinfo // Userinfo when set issues a Bearer token
Certificate *tls.Certificate // Certificate when set issues a HoK token
Lifetime time.Duration // Lifetime is the token's lifetime, defaults to 10m
Renewable bool // Renewable allows the issued token to be renewed
Delegatable bool // Delegatable allows the issued token to be delegated (e.g. for use with ActAs)
Token string // Token for Renew request or Issue request ActAs identity
}
func (c *Client) newRequest(req TokenRequest, kind string, s *Signer) (internal.RequestSecurityToken, error) {
if req.Lifetime == 0 {
req.Lifetime = 5 * time.Minute
}
created := time.Now().UTC()
rst := internal.RequestSecurityToken{
TokenType: c.Namespace,
RequestType: "http://docs.oasis-open.org/ws-sx/ws-trust/200512/" + kind,
SignatureAlgorithm: internal.SHA256,
Lifetime: &internal.Lifetime{
Created: created.Format(internal.Time),
Expires: created.Add(req.Lifetime).Format(internal.Time),
},
Renewing: &internal.Renewing{
Allow: req.Renewable,
// /wst:RequestSecurityToken/wst:Renewing/@OK
// "It NOT RECOMMENDED to use this as it can leave you open to certain types of security attacks.
// Issuers MAY restrict the period after expiration during which time the token can be renewed.
// This window is governed by the issuer's policy."
OK: false,
},
Delegatable: req.Delegatable,
}
if req.Certificate == nil {
if req.Userinfo == nil {
return rst, errors.New("one of TokenRequest Certificate or Userinfo is required")
}
rst.KeyType = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer"
} else {
rst.KeyType = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey"
rst.UseKey = &internal.UseKey{Sig: newID()}
s.keyID = rst.UseKey.Sig
}
return rst, nil
}
func (s *Signer) setLifetime(lifetime *internal.Lifetime) error {
var err error
if lifetime != nil {
s.Lifetime.Created, err = time.Parse(internal.Time, lifetime.Created)
if err == nil {
s.Lifetime.Expires, err = time.Parse(internal.Time, lifetime.Expires)
}
}
return err
}
// Issue is used to request a security token.
// The returned Signer can be used to sign SOAP requests, such as the SessionManager LoginByToken method and the RequestSecurityToken method itself.
// One of TokenRequest Certificate or Userinfo is required, with Certificate taking precedence.
// When Certificate is set, a Holder-of-Key token will be requested. Otherwise, a Bearer token is requested with the Userinfo credentials.
// See: http://docs.oasis-open.org/ws-sx/ws-trust/v1.4/errata01/os/ws-trust-1.4-errata01-os-complete.html#_Toc325658937
func (c *Client) Issue(ctx context.Context, req TokenRequest) (*Signer, error) {
s := &Signer{
Certificate: req.Certificate,
user: req.Userinfo,
}
rst, err := c.newRequest(req, "Issue", s)
if err != nil {
return nil, err
}
if req.Token != "" {
rst.ActAs = &internal.Target{
Token: req.Token,
}
}
header := soap.Header{
Security: s,
Action: rst.Action(),
}
res, err := internal.Issue(c.WithHeader(ctx, header), c, &rst)
if err != nil {
return nil, err
}
s.Token = res.RequestSecurityTokenResponse.RequestedSecurityToken.Assertion
return s, s.setLifetime(res.RequestSecurityTokenResponse.Lifetime)
}
// Renew is used to request a security token renewal.
func (c *Client) Renew(ctx context.Context, req TokenRequest) (*Signer, error) {
s := &Signer{
Certificate: req.Certificate,
}
rst, err := c.newRequest(req, "Renew", s)
if err != nil {
return nil, err
}
if req.Token == "" {
return nil, errors.New("TokenRequest Token is required")
}
rst.RenewTarget = &internal.Target{Token: req.Token}
header := soap.Header{
Security: s,
Action: rst.Action(),
}
res, err := internal.Renew(c.WithHeader(ctx, header), c, &rst)
if err != nil {
return nil, err
}
s.Token = res.RequestedSecurityToken.Assertion
return s, s.setLifetime(res.Lifetime)
}
/*
Copyright (c) 2018 VMware, Inc. All 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 sts
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"net/url"
"time"
"github.com/google/uuid"
"github.com/vmware/govmomi/sts/internal"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/xml"
)
// Signer implements the soap.Signer interface.
type Signer struct {
Token string // Token is a SAML token
Certificate *tls.Certificate // Certificate is used to sign requests
Lifetime struct {
Created time.Time
Expires time.Time
}
user *url.Userinfo // user contains the credentials for bearer token request
keyID string // keyID is the Signature UseKey ID, which is referenced in both the soap body and header
}
// signedEnvelope is similar to soap.Envelope, but with namespace and Body as innerxml
type signedEnvelope struct {
XMLName xml.Name `xml:"soap:Envelope"`
NS string `xml:"xmlns:soap,attr"`
Header soap.Header `xml:"soap:Header"`
Body string `xml:",innerxml"`
}
// newID returns a unique Reference ID, with a leading underscore as required by STS.
func newID() string {
return "_" + uuid.New().String()
}
func (s *Signer) setTokenReference(info *internal.KeyInfo) error {
var token struct {
ID string `xml:",attr"` // parse saml2:Assertion ID attribute
InnerXML string `xml:",innerxml"` // no need to parse the entire token
}
if err := xml.Unmarshal([]byte(s.Token), &token); err != nil {
return err
}
info.SecurityTokenReference = &internal.SecurityTokenReference{
WSSE11: "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd",
TokenType: "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0",
KeyIdentifier: &internal.KeyIdentifier{
ID: token.ID,
ValueType: "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID",
},
}
return nil
}
// Sign is a soap.Signer implementation which can be used to sign RequestSecurityToken and LoginByTokenBody requests.
func (s *Signer) Sign(env soap.Envelope) ([]byte, error) {
var key *rsa.PrivateKey
hasKey := false
if s.Certificate != nil {
key, hasKey = s.Certificate.PrivateKey.(*rsa.PrivateKey)
if !hasKey {
return nil, errors.New("sts: rsa.PrivateKey is required")
}
}
created := time.Now().UTC()
header := &internal.Security{
WSU: internal.WSU,
WSSE: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
Timestamp: internal.Timestamp{
NS: internal.WSU,
ID: newID(),
Created: created.Format(internal.Time),
Expires: created.Add(time.Minute).Format(internal.Time), // If STS receives this request after this, it is assumed to have expired.
},
}
env.Header.Security = header
info := internal.KeyInfo{XMLName: xml.Name{Local: "ds:KeyInfo"}}
var c14n, body string
type requestToken interface {
RequestSecurityToken() *internal.RequestSecurityToken
}
switch x := env.Body.(type) {
case requestToken:
if hasKey {
// We need c14n for all requests, as its digest is included in the signature and must match on the server side.
// We need the body in original form when using an ActAs or RenewTarget token, where the token and its signature are embedded in the body.
req := x.RequestSecurityToken()
c14n = req.C14N()
body = req.String()
id := newID()
info.SecurityTokenReference = &internal.SecurityTokenReference{
Reference: &internal.SecurityReference{
URI: "#" + id,
ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3",
},
}
header.BinarySecurityToken = &internal.BinarySecurityToken{
EncodingType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary",
ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3",
ID: id,
Value: base64.StdEncoding.EncodeToString(s.Certificate.Certificate[0]),
}
} else {
header.UsernameToken = &internal.UsernameToken{
Username: s.user.Username(),
}
header.UsernameToken.Password, _ = s.user.Password()
}
case *methods.LoginByTokenBody:
header.Assertion = s.Token
if hasKey {
if err := s.setTokenReference(&info); err != nil {
return nil, err
}
c14n = internal.Marshal(x.Req)
}
default:
// We can end up here via ssoadmin.SessionManager.Login().
// No other known cases where a signed request is needed.
header.Assertion = s.Token
if hasKey {
if err := s.setTokenReference(&info); err != nil {
return nil, err
}
type Req interface {
C14N() string
}
c14n = env.Body.(Req).C14N()
}
}
if !hasKey {
return xml.Marshal(env) // Bearer token without key to sign
}
id := newID()
tmpl := `<soap:Body xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="%s" wsu:Id="%s">%s</soap:Body>`
c14n = fmt.Sprintf(tmpl, internal.WSU, id, c14n)
if body == "" {
body = c14n
} else {
body = fmt.Sprintf(tmpl, internal.WSU, id, body)
}
header.Signature = &internal.Signature{
XMLName: xml.Name{Local: "ds:Signature"},
NS: internal.DSIG,
ID: s.keyID,
KeyInfo: info,
SignedInfo: internal.SignedInfo{
XMLName: xml.Name{Local: "ds:SignedInfo"},
NS: internal.DSIG,
CanonicalizationMethod: internal.Method{
XMLName: xml.Name{Local: "ds:CanonicalizationMethod"},
Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#",
},
SignatureMethod: internal.Method{
XMLName: xml.Name{Local: "ds:SignatureMethod"},
Algorithm: internal.SHA256,
},
Reference: []internal.Reference{
internal.NewReference(header.Timestamp.ID, header.Timestamp.C14N()),
internal.NewReference(id, c14n),
},
},
}
sum := sha256.Sum256([]byte(header.Signature.SignedInfo.C14N()))
sig, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, sum[:])
if err != nil {
return nil, err
}
header.Signature.SignatureValue = internal.Value{
XMLName: xml.Name{Local: "ds:SignatureValue"},
Value: base64.StdEncoding.EncodeToString(sig),
}
return xml.Marshal(signedEnvelope{
NS: "http://schemas.xmlsoap.org/soap/envelope/",
Header: *env.Header,
Body: body,
})
}
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"fmt"
"log"
"net/http"
"net/url"
"path"
"time"
"github.com/vmware/govmomi/sts/internal"
"github.com/vmware/govmomi/vim25/soap"
vim "github.com/vmware/govmomi/vim25/types"
)
// New creates an STS simulator and configures the simulator endpoint in the given settings.
// The path returned is that of the settings "config.vpxd.sso.sts.uri" property.
func New(u *url.URL, settings []vim.BaseOptionValue) (string, http.Handler) {
for i := range settings {
setting := settings[i].GetOptionValue()
if setting.Key == "config.vpxd.sso.sts.uri" {
endpoint, _ := url.Parse(setting.Value.(string))
endpoint.Host = u.Host
setting.Value = endpoint.String()
settings[i] = setting
return endpoint.Path, new(handler)
}
}
return "", nil
}
type handler struct{}
// ServeHTTP handles STS requests.
func (s *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
action := r.Header.Get("SOAPAction")
env := soap.Envelope{}
now := time.Now()
lifetime := &internal.Lifetime{
Created: now.Format(internal.Time),
Expires: now.Add(5 * time.Minute).Format(internal.Time),
}
switch path.Base(action) {
case "Issue":
body := internal.RequestSecurityTokenBody{
Res: &internal.RequestSecurityTokenResponseCollection{
RequestSecurityTokenResponse: internal.RequestSecurityTokenResponse{
RequestedSecurityToken: internal.RequestedSecurityToken{
Assertion: token,
},
Lifetime: lifetime,
},
},
}
env.Body = body
case "Renew":
body := internal.RenewSecurityTokenBody{
Res: &internal.RequestSecurityTokenResponse{
RequestedSecurityToken: internal.RequestedSecurityToken{
Assertion: token,
},
Lifetime: lifetime,
},
}
env.Body = body
default:
log.Printf("sts: unsupported action=%s", action)
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, internal.Marshal(env))
}
// Currently simulator.SessionManager.LoginByToken() only checks for a non-empty Assertion.Subject.NameID field,
// so the token below is returned by Issue and Renew requests for now.
var token = `<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_1881a9ba-4a76-4baa-839b-36e2cba10743" IssueInstant="2018-03-04T00:27:56.409Z" Version="2.0"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://office1-sfo2-dhcp221.eng.vmware.com/websso/SAML2/Metadata/vsphere.local</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#_1881a9ba-4a76-4baa-839b-36e2cba10743"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs xsi"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>l/0AzCGiPB69oTstUdrCkihBIDtwb83A93zAe10tG3k=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>EKHf14V0CHctwqXRlhYSYNyID5lNJLimbw57eUBm/QlAMLY7GJ1wth44oeQPSj3eMpJaXKHEYYtn
fqMngciTrq4ZP2SS7KizxuBjcHChWGmcp+t0zn7+fTbp5sL8HfF3AfOwcyZxwj8n2S7E6Eee7zeC
cjZpKKZ1QIEwASwpuMCs7vU9IuXsUguHAaN55Jpx3N5u7PlSo/NZE0TJZ+zNWP8m9H5shPDY272D
Vnp3MGfoD+Dj6T4H8OVF6bMp6czbHsEHTthwPh+pBTzR8ppkyxPKWLkC7OWiOtZBKqLSMTchQyqn
GNJdl72FBXHS8WXGtJjbwL+MKf+WujhqwdRbXw==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDxTCCAq2gAwIBAgIJAMYXe1r3pfByMA0GCSqGSIb3DQEBCwUAMIGqMQswCQYDVQQDDAJDQTEX
MBUGCgmSJomT8ixkARkWB3ZzcGhlcmUxFTATBgoJkiaJk/IsZAEZFgVsb2NhbDELMAkGA1UEBhMC
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExLDAqBgNVBAoMI29mZmljZTEtc2ZvMi1kaGNwMjIxLmVu
Zy52bXdhcmUuY29tMRswGQYDVQQLDBJWTXdhcmUgRW5naW5lZXJpbmcwHhcNMTgwMTExMjE1MjQ3
WhcNMjgwMTA2MjIwMjMxWjAYMRYwFAYDVQQDDA1zc29zZXJ2ZXJTaWduMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAohfKdXEpiCB+EewJJKk98he/KeAK/1bZ2MjnLspwt3Nvv2uh2xoa
1asP/TMAhxcztPxhqEZmi0W+nihF/yffY/AhQrGx9XynaOMUNarCNGVI2qBovi8gohT2pXlbKxgZ
b8VZkVl41WYkDBfQrzoP0XU/sFeOoNIHcFQX/82NFAYtN/4aBZ9gDqhyPihv2RSNG4MnvxxgxtZI
FPb3eyDt8poKOMjt8zG2JkJRQYiEOCLo/sKJEKXLZeWiqYsbk391/vIk2vaX3L3pgu8yYx/dLfxv
X/mRYIOcVzpXWQCEPdCejQBwrmVeRaepW5cMhOVlMAAw+mEXYVVTaIi1pfN53wIDAQABo38wfTAL
BgNVHQ8EBAMCBeAwLgYDVR0RBCcwJYIjb2ZmaWNlMS1zZm8yLWRoY3AyMjEuZW5nLnZtd2FyZS5j
b20wHQYDVR0OBBYEFAtGcFg9jVO3aBjgd2K0iBFTAPNSMB8GA1UdIwQYMBaAFLpyqy2v1I7a3URK
ohtSLAtqve5qMA0GCSqGSIb3DQEBCwUAA4IBAQB91dZHRFunBs+YvuOYFRlwJTZOPXzlSYurxC7h
VeYv6LUGZnuTkp0KfVMsfHyaeDslM8+5F9Iug1jxmEmpeyoaY12zQmxQB6P8lN4jj1Aazj8qmDH6
ClaSY4Pp0lOSp9ROVlnLi6sRsRphOg+4MS4UeXGgSFlMN1BWJmXcwCazbii8l/EzGx2QhlVjWMAz
lPFQlWQ4FvV5vUCf8iE+UTin+6oJSXmFzip1NOBOGiIbClmpergZUchNiqTYTrpqblD/Qex5Bv9e
+xAwuw8e0Lm0XICOcFmKvpotLKKiqMMsRqPoeTqnoSyKqvCGRo2hUs4Y4O6SqEd80+E5lbXImrSt</ds:X509Certificate><ds:X509Certificate>MIIEPzCCAyegAwIBAgIJANS+QleTVJNbMA0GCSqGSIb3DQEBCwUAMIGqMQswCQYDVQQDDAJDQTEX
MBUGCgmSJomT8ixkARkWB3ZzcGhlcmUxFTATBgoJkiaJk/IsZAEZFgVsb2NhbDELMAkGA1UEBhMC
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExLDAqBgNVBAoMI29mZmljZTEtc2ZvMi1kaGNwMjIxLmVu
Zy52bXdhcmUuY29tMRswGQYDVQQLDBJWTXdhcmUgRW5naW5lZXJpbmcwHhcNMTgwMTA4MjIwMjMx
WhcNMjgwMTA2MjIwMjMxWjCBqjELMAkGA1UEAwwCQ0ExFzAVBgoJkiaJk/IsZAEZFgd2c3BoZXJl
MRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh
MSwwKgYDVQQKDCNvZmZpY2UxLXNmbzItZGhjcDIyMS5lbmcudm13YXJlLmNvbTEbMBkGA1UECwwS
Vk13YXJlIEVuZ2luZWVyaW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxcN7rsoK
CIapsEOYejPK38Qk7CUSPFcOmT7iF15UFlZDogHe1G/ZkYvcP0IvLvpemRiYuRpVGVuUZ9XOgeW6
J5xpSuNRXMHSMDTUwLM9t/4NMAQxgWVlJjFmPVBIZiWaQgdCzEbCDcv/XaZeb6uJYlbmLKvopmwy
oDfncGXRUuQIZFsVIUhUgOtbbp9UmvXyjo9ukWdVcTkKlKK7NZGaVa4JYy7q4cc6g5eRmD9qp16o
vx8DageNAasTP6arnb5CyoGI4KPqJjaI7V4Z1KiOUs+Zj+VtC3XdpVthNtiJ+vgXccO8e7zYfP0y
d1PCQ/GEZAlRabus5Iplu4/xC23NywIDAQABo2YwZDAdBgNVHQ4EFgQUunKrLa/UjtrdREqiG1Is
C2q97mowHwYDVR0RBBgwFoEOZW1haWxAYWNtZS5jb22HBH8AAAEwDgYDVR0PAQH/BAQDAgEGMBIG
A1UdEwEB/wQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADggEBAC8bMIhFtlXnCF2fUixTXJ5HZFNY
vbxa1eFjLFYuBsGBqhPEHkHkdKwgpfo1sd4t0L7JaGS9wsH6zyRUQs97subV5YUI6rvAPOBGDQTm
RmCeqz3ODZq6JwZEnTTqZjvUVckmt/L/QaRUHAW27MU+SuN8rP0Nghf/gkOabsaWfyT2ADquko4e
b7seYIlR5mJs+pxVBBsBB2nzxuaV5EjkgestxBqpGkxMnKEDhG6+VjqVxsZoEiNzdBNU7eM67Jc2
2KU85jHKAao9LfMbwbHOA//1RStXXElyzPQvecq17ATvpw8AxCRu2KeKRwp3Pm2RiquDQFx8aiCe
2Re4gkrEemA=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Subject><saml2:NameID Format="http://schemas.xmlsoap.org/claims/UPN">Administrator@VSPHERE.LOCAL</saml2:NameID><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData NotOnOrAfter="2018-03-04T00:27:01.401Z"/></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="2018-03-04T00:22:01.401Z" NotOnOrAfter="2018-03-04T00:27:01.401Z"><saml2:ProxyRestriction Count="10"/></saml2:Conditions><saml2:AuthnStatement AuthnInstant="2018-03-04T00:27:56.402Z"><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement><saml2:Attribute FriendlyName="Groups" Name="http://rsa.com/schemas/attr-names/2009/01/GroupIdentity" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xsi:type="xs:string">vsphere.local\Users</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\Administrators</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\CAAdmins</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\ComponentManager.Administrators</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\SystemConfiguration.BashShellAdministrators</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\SystemConfiguration.Administrators</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\LicenseService.Administrators</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\ActAsUsers</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\Everyone</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="givenName" Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xsi:type="xs:string">Administrator</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="surname" Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xsi:type="xs:string">vsphere.local</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="Subject Type" Name="http://vmware.com/schemas/attr-names/2011/07/isSolution" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xsi:type="xs:string">false</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion>`
...@@ -19,12 +19,26 @@ package vim25 ...@@ -19,12 +19,26 @@ package vim25
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"strings"
"github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/soap" "github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types" "github.com/vmware/govmomi/vim25/types"
) )
const (
Namespace = "vim25"
Version = "6.5"
Path = "/sdk"
)
var (
ServiceInstance = types.ManagedObjectReference{
Type: "ServiceInstance",
Value: "ServiceInstance",
}
)
// Client is a tiny wrapper around the vim25/soap Client that stores session // Client is a tiny wrapper around the vim25/soap Client that stores session
// specific state (i.e. state that only needs to be retrieved once after the // specific state (i.e. state that only needs to be retrieved once after the
// client has been created). This means the client can be reused after // client has been created). This means the client can be reused after
...@@ -43,19 +57,28 @@ type Client struct { ...@@ -43,19 +57,28 @@ type Client struct {
// NewClient creates and returns a new client wirh the ServiceContent field // NewClient creates and returns a new client wirh the ServiceContent field
// filled in. // filled in.
func NewClient(ctx context.Context, rt soap.RoundTripper) (*Client, error) { func NewClient(ctx context.Context, rt soap.RoundTripper) (*Client, error) {
serviceContent, err := methods.GetServiceContent(ctx, rt)
if err != nil {
return nil, err
}
c := Client{ c := Client{
ServiceContent: serviceContent, RoundTripper: rt,
RoundTripper: rt,
} }
// Set client if it happens to be a soap.Client // Set client if it happens to be a soap.Client
if sc, ok := rt.(*soap.Client); ok { if sc, ok := rt.(*soap.Client); ok {
c.Client = sc c.Client = sc
if c.Namespace == "" {
c.Namespace = "urn:" + Namespace
} else if strings.Index(c.Namespace, ":") < 0 {
c.Namespace = "urn:" + c.Namespace // ensure valid URI format
}
if c.Version == "" {
c.Version = Version
}
}
var err error
c.ServiceContent, err = methods.GetServiceContent(ctx, rt)
if err != nil {
return nil, err
} }
return &c, nil return &c, nil
......
...@@ -24,14 +24,15 @@ import ( ...@@ -24,14 +24,15 @@ import (
"github.com/vmware/govmomi/vim25/types" "github.com/vmware/govmomi/vim25/types"
) )
var ServiceInstance = types.ManagedObjectReference{ // copy of vim25.ServiceInstance to avoid import cycle
var serviceInstance = types.ManagedObjectReference{
Type: "ServiceInstance", Type: "ServiceInstance",
Value: "ServiceInstance", Value: "ServiceInstance",
} }
func GetServiceContent(ctx context.Context, r soap.RoundTripper) (types.ServiceContent, error) { func GetServiceContent(ctx context.Context, r soap.RoundTripper) (types.ServiceContent, error) {
req := types.RetrieveServiceContent{ req := types.RetrieveServiceContent{
This: ServiceInstance, This: serviceInstance,
} }
res, err := RetrieveServiceContent(ctx, r, &req) res, err := RetrieveServiceContent(ctx, r, &req)
...@@ -44,7 +45,7 @@ func GetServiceContent(ctx context.Context, r soap.RoundTripper) (types.ServiceC ...@@ -44,7 +45,7 @@ func GetServiceContent(ctx context.Context, r soap.RoundTripper) (types.ServiceC
func GetCurrentTime(ctx context.Context, r soap.RoundTripper) (*time.Time, error) { func GetCurrentTime(ctx context.Context, r soap.RoundTripper) (*time.Time, error) {
req := types.CurrentTime{ req := types.CurrentTime{
This: ServiceInstance, This: serviceInstance,
} }
res, err := CurrentTime(ctx, r, &req) res, err := CurrentTime(ctx, r, &req)
......
/* /*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved. Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -164,6 +164,22 @@ func init() { ...@@ -164,6 +164,22 @@ func init() {
t["CryptoManager"] = reflect.TypeOf((*CryptoManager)(nil)).Elem() t["CryptoManager"] = reflect.TypeOf((*CryptoManager)(nil)).Elem()
} }
type CryptoManagerHost struct {
CryptoManager
}
func init() {
t["CryptoManagerHost"] = reflect.TypeOf((*CryptoManagerHost)(nil)).Elem()
}
type CryptoManagerHostKMS struct {
CryptoManagerHost
}
func init() {
t["CryptoManagerHostKMS"] = reflect.TypeOf((*CryptoManagerHostKMS)(nil)).Elem()
}
type CryptoManagerKmip struct { type CryptoManagerKmip struct {
CryptoManager CryptoManager
...@@ -759,9 +775,10 @@ func init() { ...@@ -759,9 +775,10 @@ func init() {
type HostGraphicsManager struct { type HostGraphicsManager struct {
ExtensibleManagedObject ExtensibleManagedObject
GraphicsInfo []types.HostGraphicsInfo `mo:"graphicsInfo"` GraphicsInfo []types.HostGraphicsInfo `mo:"graphicsInfo"`
GraphicsConfig *types.HostGraphicsConfig `mo:"graphicsConfig"` GraphicsConfig *types.HostGraphicsConfig `mo:"graphicsConfig"`
SharedPassthruGpuTypes []string `mo:"sharedPassthruGpuTypes"` SharedPassthruGpuTypes []string `mo:"sharedPassthruGpuTypes"`
SharedGpuCapabilities []types.HostSharedGpuCapabilities `mo:"sharedGpuCapabilities"`
} }
func init() { func init() {
...@@ -853,6 +870,20 @@ func init() { ...@@ -853,6 +870,20 @@ func init() {
t["HostNetworkSystem"] = reflect.TypeOf((*HostNetworkSystem)(nil)).Elem() t["HostNetworkSystem"] = reflect.TypeOf((*HostNetworkSystem)(nil)).Elem()
} }
type HostNvdimmSystem struct {
Self types.ManagedObjectReference
NvdimmSystemInfo types.NvdimmSystemInfo `mo:"nvdimmSystemInfo"`
}
func (m HostNvdimmSystem) Reference() types.ManagedObjectReference {
return m.Self
}
func init() {
t["HostNvdimmSystem"] = reflect.TypeOf((*HostNvdimmSystem)(nil)).Elem()
}
type HostPatchManager struct { type HostPatchManager struct {
Self types.ManagedObjectReference Self types.ManagedObjectReference
} }
...@@ -894,7 +925,10 @@ func init() { ...@@ -894,7 +925,10 @@ func init() {
type HostProfile struct { type HostProfile struct {
Profile Profile
ReferenceHost *types.ManagedObjectReference `mo:"referenceHost"` ValidationState *string `mo:"validationState"`
ValidationStateUpdateTime *time.Time `mo:"validationStateUpdateTime"`
ValidationFailureInfo *types.HostProfileValidationFailureInfo `mo:"validationFailureInfo"`
ReferenceHost *types.ManagedObjectReference `mo:"referenceHost"`
} }
func init() { func init() {
...@@ -962,18 +996,25 @@ func init() { ...@@ -962,18 +996,25 @@ func init() {
type HostSystem struct { type HostSystem struct {
ManagedEntity ManagedEntity
Runtime types.HostRuntimeInfo `mo:"runtime"` Runtime types.HostRuntimeInfo `mo:"runtime"`
Summary types.HostListSummary `mo:"summary"` Summary types.HostListSummary `mo:"summary"`
Hardware *types.HostHardwareInfo `mo:"hardware"` Hardware *types.HostHardwareInfo `mo:"hardware"`
Capability *types.HostCapability `mo:"capability"` Capability *types.HostCapability `mo:"capability"`
LicensableResource types.HostLicensableResourceInfo `mo:"licensableResource"` LicensableResource types.HostLicensableResourceInfo `mo:"licensableResource"`
ConfigManager types.HostConfigManager `mo:"configManager"` RemediationState *types.HostSystemRemediationState `mo:"remediationState"`
Config *types.HostConfigInfo `mo:"config"` PrecheckRemediationResult *types.ApplyHostProfileConfigurationSpec `mo:"precheckRemediationResult"`
Vm []types.ManagedObjectReference `mo:"vm"` RemediationResult *types.ApplyHostProfileConfigurationResult `mo:"remediationResult"`
Datastore []types.ManagedObjectReference `mo:"datastore"` ComplianceCheckState *types.HostSystemComplianceCheckState `mo:"complianceCheckState"`
Network []types.ManagedObjectReference `mo:"network"` ComplianceCheckResult *types.ComplianceResult `mo:"complianceCheckResult"`
DatastoreBrowser types.ManagedObjectReference `mo:"datastoreBrowser"` ConfigManager types.HostConfigManager `mo:"configManager"`
SystemResources *types.HostSystemResourceInfo `mo:"systemResources"` Config *types.HostConfigInfo `mo:"config"`
Vm []types.ManagedObjectReference `mo:"vm"`
Datastore []types.ManagedObjectReference `mo:"datastore"`
Network []types.ManagedObjectReference `mo:"network"`
DatastoreBrowser types.ManagedObjectReference `mo:"datastoreBrowser"`
SystemResources *types.HostSystemResourceInfo `mo:"systemResources"`
AnswerFileValidationState *types.AnswerFileStatusResult `mo:"answerFileValidationState"`
AnswerFileValidationResult *types.AnswerFileStatusResult `mo:"answerFileValidationResult"`
} }
func (m *HostSystem) Entity() *ManagedEntity { func (m *HostSystem) Entity() *ManagedEntity {
...@@ -1056,10 +1097,13 @@ func init() { ...@@ -1056,10 +1097,13 @@ func init() {
type HttpNfcLease struct { type HttpNfcLease struct {
Self types.ManagedObjectReference Self types.ManagedObjectReference
InitializeProgress int32 `mo:"initializeProgress"` InitializeProgress int32 `mo:"initializeProgress"`
Info *types.HttpNfcLeaseInfo `mo:"info"` TransferProgress int32 `mo:"transferProgress"`
State types.HttpNfcLeaseState `mo:"state"` Mode string `mo:"mode"`
Error *types.LocalizedMethodFault `mo:"error"` Capabilities types.HttpNfcLeaseCapabilities `mo:"capabilities"`
Info *types.HttpNfcLeaseInfo `mo:"info"`
State types.HttpNfcLeaseState `mo:"state"`
Error *types.LocalizedMethodFault `mo:"error"`
} }
func (m HttpNfcLease) Reference() types.ManagedObjectReference { func (m HttpNfcLease) Reference() types.ManagedObjectReference {
......
...@@ -100,11 +100,12 @@ func NewReader(ctx context.Context, s Sinker, r io.Reader, size int64) *reader { ...@@ -100,11 +100,12 @@ func NewReader(ctx context.Context, s Sinker, r io.Reader, size int64) *reader {
// underlying channel. // underlying channel.
func (r *reader) Read(b []byte) (int, error) { func (r *reader) Read(b []byte) (int, error) {
n, err := r.r.Read(b) n, err := r.r.Read(b)
if err != nil { r.pos += int64(n)
if err != nil && err != io.EOF {
return n, err return n, err
} }
r.pos += int64(n)
q := readerReport{ q := readerReport{
t: time.Now(), t: time.Now(),
pos: r.pos, pos: r.pos,
......
...@@ -28,6 +28,7 @@ import ( ...@@ -28,6 +28,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"net" "net"
"net/http" "net/http"
"net/http/cookiejar" "net/http/cookiejar"
...@@ -53,17 +54,9 @@ type RoundTripper interface { ...@@ -53,17 +54,9 @@ type RoundTripper interface {
} }
const ( const (
DefaultVimNamespace = "urn:vim25" SessionCookieName = "vmware_soap_session"
DefaultVimVersion = "6.5"
DefaultMinVimVersion = "5.5"
SessionCookieName = "vmware_soap_session"
) )
type header struct {
Cookie string `xml:"vcSessionCookie,omitempty"`
ID string `xml:"operationID,omitempty"`
}
type Client struct { type Client struct {
http.Client http.Client
...@@ -71,7 +64,6 @@ type Client struct { ...@@ -71,7 +64,6 @@ type Client struct {
k bool // Named after curl's -k flag k bool // Named after curl's -k flag
d *debugContainer d *debugContainer
t *http.Transport t *http.Transport
p *url.URL
hostsMu sync.Mutex hostsMu sync.Mutex
hosts map[string]string hosts map[string]string
...@@ -149,20 +141,33 @@ func NewClient(u *url.URL, insecure bool) *Client { ...@@ -149,20 +141,33 @@ func NewClient(u *url.URL, insecure bool) *Client {
c.u = c.URL() c.u = c.URL()
c.u.User = nil c.u.User = nil
c.Namespace = DefaultVimNamespace
c.Version = DefaultVimVersion
return &c return &c
} }
// NewServiceClient creates a NewClient with the given URL.Path and namespace. // NewServiceClient creates a NewClient with the given URL.Path and namespace.
func (c *Client) NewServiceClient(path string, namespace string) *Client { func (c *Client) NewServiceClient(path string, namespace string) *Client {
u := c.URL() vc := c.URL()
u.Path = path u, err := url.Parse(path)
if err != nil {
log.Panicf("url.Parse(%q): %s", path, err)
}
if u.Host == "" {
u.Scheme = vc.Scheme
u.Host = vc.Host
}
client := NewClient(u, c.k) client := NewClient(u, c.k)
client.Namespace = "urn:" + namespace
if cert := c.Certificate(); cert != nil {
client.SetCertificate(*cert)
}
client.Namespace = namespace // Copy the trusted thumbprints
c.hostsMu.Lock()
for k, v := range c.hosts {
client.hosts[k] = v
}
c.hostsMu.Unlock()
// Copy the cookies // Copy the cookies
client.Client.Jar.SetCookies(u, c.Client.Jar.Cookies(u)) client.Client.Jar.SetCookies(u, c.Client.Jar.Cookies(u))
...@@ -175,6 +180,9 @@ func (c *Client) NewServiceClient(path string, namespace string) *Client { ...@@ -175,6 +180,9 @@ func (c *Client) NewServiceClient(path string, namespace string) *Client {
} }
} }
// Copy any query params (e.g. GOVMOMI_TUNNEL_PROXY_PORT used in testing)
client.u.RawQuery = vc.RawQuery
return client return client
} }
...@@ -346,19 +354,33 @@ func splitHostPort(host string) (string, string) { ...@@ -346,19 +354,33 @@ func splitHostPort(host string) (string, string) {
const sdkTunnel = "sdkTunnel:8089" const sdkTunnel = "sdkTunnel:8089"
func (c *Client) Certificate() *tls.Certificate {
certs := c.t.TLSClientConfig.Certificates
if len(certs) == 0 {
return nil
}
return &certs[0]
}
func (c *Client) SetCertificate(cert tls.Certificate) { func (c *Client) SetCertificate(cert tls.Certificate) {
t := c.Client.Transport.(*http.Transport) t := c.Client.Transport.(*http.Transport)
// Extension certificate // Extension or HoK certificate
t.TLSClientConfig.Certificates = []tls.Certificate{cert} t.TLSClientConfig.Certificates = []tls.Certificate{cert}
}
// Tunnel returns a Client configured to proxy requests through vCenter's http port 80,
// to the SDK tunnel virtual host. Use of the SDK tunnel is required by LoginExtensionByCertificate()
// and optional for other methods.
func (c *Client) Tunnel() *Client {
tunnel := c.NewServiceClient(c.u.Path, c.Namespace)
t := tunnel.Client.Transport.(*http.Transport)
// Proxy to vCenter host on port 80 // Proxy to vCenter host on port 80
host, _ := splitHostPort(c.u.Host) host := tunnel.u.Hostname()
// Should be no reason to change the default port other than testing // Should be no reason to change the default port other than testing
key := "GOVMOMI_TUNNEL_PROXY_PORT" key := "GOVMOMI_TUNNEL_PROXY_PORT"
port := c.URL().Query().Get(key) port := tunnel.URL().Query().Get(key)
if port == "" { if port == "" {
port = os.Getenv(key) port = os.Getenv(key)
} }
...@@ -367,20 +389,14 @@ func (c *Client) SetCertificate(cert tls.Certificate) { ...@@ -367,20 +389,14 @@ func (c *Client) SetCertificate(cert tls.Certificate) {
host += ":" + port host += ":" + port
} }
c.p = &url.URL{ t.Proxy = http.ProxyURL(&url.URL{
Scheme: "http", Scheme: "http",
Host: host, Host: host,
} })
t.Proxy = func(r *http.Request) (*url.URL, error) {
// Only sdk requests should be proxied
if r.URL.Path == "/sdk" {
return c.p, nil
}
return http.ProxyFromEnvironment(r)
}
// Rewrite url Host to use the sdk tunnel, required for a certificate request. // Rewrite url Host to use the sdk tunnel, required for a certificate request.
c.u.Host = sdkTunnel tunnel.u.Host = sdkTunnel
return tunnel
} }
func (c *Client) URL() *url.URL { func (c *Client) URL() *url.URL {
...@@ -426,21 +442,41 @@ func (c *Client) do(ctx context.Context, req *http.Request) (*http.Response, err ...@@ -426,21 +442,41 @@ func (c *Client) do(ctx context.Context, req *http.Request) (*http.Response, err
return c.Client.Do(req.WithContext(ctx)) return c.Client.Do(req.WithContext(ctx))
} }
// Signer can be implemented by soap.Header.Security to sign requests.
// If the soap.Header.Security field is set to an implementation of Signer via WithHeader(),
// then Client.RoundTrip will call Sign() to marshal the SOAP request.
type Signer interface {
Sign(Envelope) ([]byte, error)
}
type headerContext struct{}
// WithHeader can be used to modify the outgoing request soap.Header fields.
func (c *Client) WithHeader(ctx context.Context, header Header) context.Context {
return context.WithValue(ctx, headerContext{}, header)
}
func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error { func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error {
var err error var err error
var b []byte
reqEnv := Envelope{Body: reqBody} reqEnv := Envelope{Body: reqBody}
resEnv := Envelope{Body: resBody} resEnv := Envelope{Body: resBody}
h := &header{ h, ok := ctx.Value(headerContext{}).(Header)
Cookie: c.cookie, if !ok {
h = Header{}
} }
// We added support for OperationID before soap.Header was exported.
if id, ok := ctx.Value(types.ID{}).(string); ok { if id, ok := ctx.Value(types.ID{}).(string); ok {
h.ID = id h.ID = id
} }
reqEnv.Header = h h.Cookie = c.cookie
if h.Cookie != "" || h.ID != "" || h.Security != nil {
reqEnv.Header = &h // XML marshal header only if a field is set
}
// Create debugging context for this round trip // Create debugging context for this round trip
d := c.d.newRoundTrip() d := c.d.newRoundTrip()
...@@ -448,9 +484,16 @@ func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error ...@@ -448,9 +484,16 @@ func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error
defer d.done() defer d.done()
} }
b, err := xml.Marshal(reqEnv) if signer, ok := h.Security.(Signer); ok {
if err != nil { b, err = signer.Sign(reqEnv)
panic(err) if err != nil {
return err
}
} else {
b, err = xml.Marshal(reqEnv)
if err != nil {
panic(err)
}
} }
rawReqBody := io.MultiReader(strings.NewReader(xml.Header), bytes.NewReader(b)) rawReqBody := io.MultiReader(strings.NewReader(xml.Header), bytes.NewReader(b))
...@@ -462,8 +505,13 @@ func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error ...@@ -462,8 +505,13 @@ func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error
req = req.WithContext(ctx) req = req.WithContext(ctx)
req.Header.Set(`Content-Type`, `text/xml; charset="utf-8"`) req.Header.Set(`Content-Type`, `text/xml; charset="utf-8"`)
soapAction := fmt.Sprintf("%s/%s", c.Namespace, c.Version)
req.Header.Set(`SOAPAction`, soapAction) action := h.Action
if action == "" {
action = fmt.Sprintf("%s/%s", c.Namespace, c.Version)
}
req.Header.Set(`SOAPAction`, action)
if c.UserAgent != "" { if c.UserAgent != "" {
req.Header.Set(`User-Agent`, c.UserAgent) req.Header.Set(`User-Agent`, c.UserAgent)
} }
......
/* /*
Copyright (c) 2014 VMware, Inc. All Rights Reserved. Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -21,9 +21,17 @@ import ( ...@@ -21,9 +21,17 @@ import (
"github.com/vmware/govmomi/vim25/xml" "github.com/vmware/govmomi/vim25/xml"
) )
// Header includes optional soap Header fields.
type Header struct {
Action string `xml:"-"` // Action is the 'SOAPAction' HTTP header value. Defaults to "Client.Namespace/Client.Version".
Cookie string `xml:"vcSessionCookie,omitempty"` // Cookie is a vCenter session cookie that can be used with other SDK endpoints (e.g. pbm).
ID string `xml:"operationID,omitempty"` // ID is the operationID used by ESX/vCenter logging for correlation.
Security interface{} `xml:",omitempty"` // Security is used for SAML token authentication and request signing.
}
type Envelope struct { type Envelope struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
Header interface{} `xml:",omitempty"` Header *Header `xml:"http://schemas.xmlsoap.org/soap/envelope/ Header,omitempty"`
Body interface{} Body interface{}
} }
......
/* /*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved. Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment