Commit c340f6f9 authored by Doug MacEachern's avatar Doug MacEachern

vSphere Cloud Provider: update vmware/gomvomi godeps

parent 83768d28
......@@ -2677,113 +2677,148 @@
},
{
"ImportPath": "github.com/vmware/govmomi",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/find",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/list",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"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",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/object",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/pbm",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/pbm/methods",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/pbm/types",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/property",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/session",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/simulator",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/simulator/esx",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/simulator/vpx",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"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",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/debug",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/methods",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/mo",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/progress",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/soap",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/types",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/xml",
"Comment": "v0.16.0-97-g0f82f03",
"Rev": "0f82f03a2bbf14037d2331cf02f1d4157bbef6cc"
"Comment": "v0.17.1-46-ge70dd44",
"Rev": "e70dd44f80baf671099254d675eb278529038234"
},
{
"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
language: go
go:
- 1.8
- 1.8.x
- 1.9.x
- '1.10'
go_import_path: github.com/vmware/govmomi
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y xmlstarlet
- make vendor
script:
- make check test
- 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
### 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)
* Add support for SOAP request operation ID header
......
......@@ -6,8 +6,10 @@
Abhijeet Kasurde <akasurde@redhat.com>
abrarshivani <abrarshivani@users.noreply.github.com>
Adam Shannon <adamkshannon@gmail.com>
Alessandro Cortiana <alessandro.cortiana@gmail.com>
Alex Bozhenko <alexbozhenko@fb.com>
Alvaro Miranda <kikitux@gmail.com>
amandahla <amanda.andrade@serpro.gov.br>
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br>
Amit Bathla <abathla@.vmware.com>
amit bezalel <amit.bezalel@hpe.com>
Andrew Chin <andrew@andrewtchin.com>
......@@ -21,17 +23,19 @@ bastienbc <bastien.barbe.creuly@gmail.com>
Bob Killen <killen.bob@gmail.com>
Brad Fitzpatrick <bradfitz@golang.org>
Bruce Downs <bruceadowns@gmail.com>
Cédric Blomart <cblomart@gmail.com>
Chris Marchesi <chrism@vancluevertech.com>
Christian Höltje <docwhat@gerf.org>
Clint Greenwood <cgreenwood@vmware.com>
Cédric Blomart <cblomart@gmail.com>
Danny Lockard <danny.lockard@banno.com>
Dave Tucker <dave@dtucker.co.uk>
Davide Agnello <dagnello@hp.com>
David Stark <dave@davidstark.name>
Davide Agnello <dagnello@hp.com>
Doug MacEachern <dougm@vmware.com>
Eloy Coto <eloy.coto@gmail.com>
Eric Gray <egray@vmware.com>
Eric Yutao <eric.yutao@gmail.com>
Erik Hollensbe <github@hollensbe.org>
Fabio Rapposelli <fabio@vmware.com>
Faiyaz Ahmed <ahmedf@vmware.com>
forkbomber <forkbomber@users.noreply.github.com>
......@@ -46,11 +50,14 @@ Isaac Rodman <isaac@eyz.us>
Ivan Porto Carrero <icarrero@vmware.com>
Jason Kincl <jkincl@gmail.com>
Jeremy Canady <jcanady@jackhenry.com>
jeremy-clerc <jeremy@clerc.io>
João Pereira <joaodrp@gmail.com>
Louie Jiang <jiangl@vmware.com>
Marc Carmier <mcarmier@gmail.com>
Matthew Cosgrove <matthew.cosgrove@dell.com>
Mevan Samaratunga <mevansam@gmail.com>
Nicolas Lamirault <nicolas.lamirault@gmail.com>
Parham Alvani <parham.alvani@gmail.com>
Pieter Noordhuis <pnoordhuis@vmware.com>
runner.mei <runner.mei@gmail.com>
S.Çağlar Onur <conur@vmware.com>
......@@ -60,7 +67,10 @@ Takaaki Furukawa <takaaki.frkw@gmail.com>
tanishi <tanishi503@gmail.com>
Ted Zlatanov <tzz@lifelogs.com>
Thibaut Ackermann <thibaut.ackermann@alcatel-lucent.com>
Trevor Dawe <trevor.dawe@gmail.com>
Vadim Egorov <vegorov@vmware.com>
Volodymyr Bobyr <pupsua@gmail.com>
Witold Krecicki <wpk@culm.net>
Yang Yang <yangy@vmware.com>
Yuya Kusakabe <yuya.kusakabe@gmail.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
import (
"context"
"crypto/tls"
"net/url"
"github.com/vmware/govmomi/property"
......@@ -99,41 +98,11 @@ func NewClient(ctx context.Context, u *url.URL, insecure bool) (*Client, error)
return c, nil
}
// NewClientWithCertificate creates a new client from a URL. The client authenticates with the
// server with the certificate before returning if the URL contains user information.
func NewClientWithCertificate(ctx context.Context, u *url.URL, insecure bool, cert tls.Certificate) (*Client, error) {
soapClient := soap.NewClient(u, insecure)
soapClient.SetCertificate(cert)
vimClient, err := vim25.NewClient(ctx, soapClient)
if err != nil {
return nil, err
}
c := &Client{
Client: vimClient,
SessionManager: session.NewManager(vimClient),
}
if u.User != nil {
err = c.LoginExtensionByCertificate(ctx, u.User.Username(), "")
if err != nil {
return nil, err
}
}
return c, nil
}
// Login dispatches to the SessionManager.
func (c *Client) Login(ctx context.Context, u *url.Userinfo) error {
return c.SessionManager.Login(ctx, u)
}
// Login dispatches to the SessionManager.
func (c *Client) LoginExtensionByCertificate(ctx context.Context, key string, locale string) error {
return c.SessionManager.LoginExtensionByCertificate(ctx, key, locale)
}
// Logout dispatches to the SessionManager.
func (c *Client) Logout(ctx context.Context) error {
// Close any idle connections after logging out.
......
......@@ -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) {
hs, err := f.HostSystem(ctx, "*/*")
hs, err := f.HostSystem(ctx, "*")
if err != nil {
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
import (
"context"
"fmt"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
......@@ -39,11 +40,17 @@ func NewDistributedVirtualPortgroup(c *vim25.Client, ref types.ManagedObjectRefe
func (p DistributedVirtualPortgroup) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
var dvp mo.DistributedVirtualPortgroup
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
}
// "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 {
return nil, err
}
......
......@@ -66,9 +66,10 @@ func (s DistributedVirtualSwitch) AddPortgroup(ctx context.Context, spec []types
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{
This: s.Reference(),
This: s.Reference(),
Criteria: criteria,
}
res, err := methods.FetchDVPorts(ctx, s.Client(), req)
......
......@@ -61,7 +61,10 @@ func EthernetCardTypes() VirtualDeviceList {
return VirtualDeviceList([]types.BaseVirtualDevice{
&types.VirtualE1000{},
&types.VirtualE1000e{},
&types.VirtualVmxnet2{},
&types.VirtualVmxnet3{},
&types.VirtualPCNet32{},
&types.VirtualSriovEthernetCard{},
}).Select(func(device types.BaseVirtualDevice) bool {
c := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
c.GetVirtualDevice().Key = -1
......@@ -834,7 +837,7 @@ func (l VirtualDeviceList) TypeName(device types.BaseVirtualDevice) string {
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 {
name := "device"
......
......@@ -46,9 +46,10 @@ type queryVirtualDiskInfoTaskResponse struct {
}
type queryVirtualDiskInfoTaskBody struct {
Req *queryVirtualDiskInfoTaskRequest `xml:"urn:internalvim25 QueryVirtualDiskInfo_Task,omitempty"`
Res *queryVirtualDiskInfoTaskResponse `xml:"urn:vim25 QueryVirtualDiskInfo_TaskResponse,omitempty"`
Err *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
Req *queryVirtualDiskInfoTaskRequest `xml:"urn:internalvim25 QueryVirtualDiskInfo_Task,omitempty"`
Res *queryVirtualDiskInfoTaskResponse `xml:"urn:vim25 QueryVirtualDiskInfo_TaskResponse,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 }
......@@ -62,7 +63,11 @@ func queryVirtualDiskInfoTask(ctx context.Context, r soap.RoundTripper, req *que
return nil, err
}
return resBody.Res, nil
if resBody.Res != nil {
return resBody.Res, nil
}
return resBody.InternalRes, nil
}
type VirtualDiskInfo struct {
......
......@@ -27,6 +27,18 @@ import (
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 {
*soap.Client
......@@ -34,13 +46,10 @@ type Client struct {
}
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{
This: vim.ManagedObjectReference{
Type: "PbmServiceInstance",
Value: "ServiceInstance",
},
This: ServiceInstance,
}
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");
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");
you may not use this file except in compliance with the License.
......@@ -152,6 +152,20 @@ func init() {
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
const (
......@@ -179,6 +193,7 @@ type PbmSystemCreatedProfileType string
const (
PbmSystemCreatedProfileTypeVsanDefaultProfile = PbmSystemCreatedProfileType("VsanDefaultProfile")
PbmSystemCreatedProfileTypeVVolDefaultProfile = PbmSystemCreatedProfileType("VVolDefaultProfile")
PbmSystemCreatedProfileTypePmemDefaultProfile = PbmSystemCreatedProfileType("PmemDefaultProfile")
)
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");
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");
you may not use this file except in compliance with the License.
......@@ -269,6 +269,12 @@ func init() {
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
func init() {
......@@ -1710,3 +1716,9 @@ type PbmVaioDataServiceInfo struct {
func init() {
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 {
}
}
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.
......
......@@ -18,6 +18,7 @@ package session
import (
"context"
"net/http"
"net/url"
"os"
......@@ -89,14 +90,51 @@ func (sm *Manager) Login(ctx context.Context, u *url.Userinfo) error {
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{
This: sm.Reference(),
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 {
return err
}
......
......@@ -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
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.Summary.PortgroupName = append(s.Summary.PortgroupName, pg.Name)
......@@ -111,10 +119,13 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
Map.AddReference(pg, &pg.Host, member.Host)
}
case types.ConfigSpecOperationRemove:
if pg := FindReference(host.Network, s.Portgroup...); pg != nil {
return nil, &types.ResourceInUse{
Type: pg.Type,
Name: pg.Value,
for _, ref := range host.Vm {
vm := Map.Get(ref).(*VirtualMachine)
if pg := FindReference(vm.Network, s.Portgroup...); pg != nil {
return nil, &types.ResourceInUse{
Type: pg.Type,
Name: pg.Value,
}
}
}
......@@ -170,10 +181,6 @@ func (s *DistributedVirtualSwitch) dvPortgroups(_ *types.DistributedVirtualSwitc
},
})
if pg.PortKeys == nil {
continue
}
for _, key := range pg.PortKeys {
res = append(res, types.DistributedVirtualPort{
DvsUuid: s.Uuid,
......
......@@ -63,17 +63,14 @@ func NewEventManager(ref types.ManagedObjectReference) object.Reference {
}
}
func (m *EventManager) CreateCollectorForEvents(ctx *Context, req *types.CreateCollectorForEvents) soap.HasFault {
body := new(methods.CreateCollectorForEventsBody)
func (m *EventManager) createCollector(ctx *Context, req *types.CreateCollectorForEvents) (*EventHistoryCollector, *soap.Fault) {
size, err := validatePageSize(req.Filter.MaxCount)
if err != nil {
body.Fault_ = err
return body
return nil, err
}
if len(m.collectors) >= int(m.MaxCollector) {
body.Fault_ = Fault("Too many event collectors to create", new(types.InvalidState))
return body
return nil, Fault("Too many event collectors to create", new(types.InvalidState))
}
collector := &EventHistoryCollector{
......@@ -83,6 +80,17 @@ func (m *EventManager) CreateCollectorForEvents(ctx *Context, req *types.CreateC
collector.Filter = req.Filter
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()
m.collectors[ref] = collector
......@@ -93,6 +101,27 @@ func (m *EventManager) CreateCollectorForEvents(ctx *Context, req *types.CreateC
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.
func (m *EventManager) formatMessage(event types.BaseEvent) {
id := reflect.ValueOf(event).Elem().Type().Name()
......@@ -372,15 +401,23 @@ func (c *EventHistoryCollector) DestroyCollector(ctx *Context, req *types.Destro
}
}
func (c *EventHistoryCollector) Get() mo.Reference {
clone := *c
func (c *EventHistoryCollector) GetLatestPage() []types.BaseEvent {
var latestPage []types.BaseEvent
c.page.Do(func(val interface{}) {
if val == nil {
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
}
......@@ -20,6 +20,7 @@ import (
"fmt"
"math/rand"
"path"
"strings"
"github.com/google/uuid"
......@@ -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
})
......@@ -527,3 +539,45 @@ func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault {
func (f *Folder) RenameTask(r *types.Rename_Task) soap.HasFault {
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
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 {
task := CreateTask(h, "enterMaintenanceMode", func(t *Task) (types.AnyType, types.BaseMethodFault) {
h.Runtime.InMaintenanceMode = true
......
......@@ -111,17 +111,29 @@ func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{}
pval = &types.ArrayOfString{
String: v,
}
case []uint8:
pval = &types.ArrayOfByte{
Byte: v,
}
case []int16:
pval = &types.ArrayOfShort{
Short: v,
}
case []int32:
pval = &types.ArrayOfInt{
Int: v,
}
case []int64:
pval = &types.ArrayOfLong{
Long: v,
}
default:
kind := f.Type.Elem().Name()
// Remove govmomi interface prefix name
if strings.HasPrefix(kind, "Base") {
kind = kind[4:]
}
akind, _ := typeFunc("ArrayOf" + kind)
akind, _ := defaultMapType("ArrayOf" + kind)
a := reflect.New(akind)
a.Elem().FieldByName(kind).Set(rval)
pval = a.Interface()
......@@ -278,14 +290,10 @@ func (rr *retrieveResult) collectFields(ctx *Context, rval reflect.Value, fields
seen[name] = true
val, err := fieldValue(rval, name)
if err == nil {
rr.add(ctx, name, val, content)
continue
}
switch err {
case errEmptyField:
// ok
case nil, errEmptyField:
rr.add(ctx, name, val, content)
case errMissingField:
content.MissingSet = append(content.MissingSet, types.MissingProperty{
Path: name,
......@@ -469,6 +477,19 @@ func (pc *PropertyCollector) RetrievePropertiesEx(ctx *Context, r *types.Retriev
if fault != nil {
body.Fault_ = Fault("", fault)
} 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{
Returnval: res,
}
......
......@@ -24,7 +24,7 @@ import (
"strings"
"sync"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
......@@ -40,6 +40,7 @@ var refValueMap = map[string]string{
"VirtualMachine": "vm",
"VirtualMachineSnapshot": "snapshot",
"VmwareDistributedVirtualSwitch": "dvs",
"DistributedVirtualSwitch": "dvs",
}
// Map is the default Registry instance.
......@@ -59,6 +60,9 @@ type Registry struct {
handlers map[types.ManagedObjectReference]RegisterObject
locks map[types.ManagedObjectReference]sync.Locker
counter int
Namespace string
Path string
}
// NewRegistry creates a new instances of Registry
......@@ -67,11 +71,23 @@ func NewRegistry() *Registry {
objects: make(map[types.ManagedObjectReference]mo.Reference),
handlers: make(map[types.ManagedObjectReference]RegisterObject),
locks: make(map[types.ManagedObjectReference]sync.Locker),
Namespace: vim25.Namespace,
Path: vim25.Path,
}
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.
func typeName(item mo.Reference) string {
return reflect.TypeOf(item).Elem().Name()
......@@ -332,7 +348,7 @@ func (r *Registry) removeString(obj mo.Reference, field *[]string, val string) {
}
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
......@@ -380,6 +396,11 @@ func (r *Registry) SessionManager() *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) {
r.m.Lock()
defer r.m.Unlock()
......
......@@ -19,8 +19,10 @@ package simulator
import (
"time"
"github.com/google/uuid"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/vpx"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
......@@ -36,7 +38,7 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service
s := &ServiceInstance{}
s.Self = methods.ServiceInstance
s.Self = vim25.ServiceInstance
s.Content = content
Map.Put(s)
......@@ -49,6 +51,7 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service
if content.About.ApiType == "HostAgent" {
CreateDefaultESX(f)
} else {
content.About.InstanceUuid = uuid.New().String()
setting = vpx.Setting
}
......
......@@ -49,33 +49,85 @@ func NewSessionManager(ref types.ManagedObjectReference) object.Reference {
return s
}
func (s *SessionManager) Login(ctx *Context, login *types.Login) soap.HasFault {
body := &methods.LoginBody{}
func createSession(ctx *Context, name string, locale string) types.UserSession {
now := time.Now().UTC()
if login.Locale == "" {
login.Locale = session.Locale
if 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
} else {
session := Session{
UserSession: types.UserSession{
Key: uuid.New().String(),
UserName: login.UserName,
FullName: login.UserName,
LoginTime: time.Now(),
LastActiveTime: time.Now(),
Locale: login.Locale,
MessageLocale: login.Locale,
},
Registry: NewRegistry(),
body.Res = &types.LoginResponse{
Returnval: createSession(ctx, req.UserName, req.Locale),
}
}
ctx.SetSession(session, true)
return body
}
body.Res = &types.LoginResponse{
Returnval: session.UserSession,
func (s *SessionManager) LoginExtensionByCertificate(ctx *Context, req *types.LoginExtensionByCertificate) soap.HasFault {
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{
},
Registry: NewRegistry(),
},
Map: Map,
}
var invalidLogin = Fault("Login failure", new(types.InvalidLogin))
......@@ -175,7 +228,9 @@ type Context struct {
context.Context
Session *Session
Header soap.Header
Caller *types.ManagedObjectReference
Map *Registry
}
// mapSession maps an HTTP cookie to a Session.
......
......@@ -21,6 +21,7 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"flag"
......@@ -28,6 +29,7 @@ import (
"io"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httptest"
"net/url"
......@@ -35,6 +37,7 @@ import (
"path"
"reflect"
"sort"
"strconv"
"strings"
"github.com/vmware/govmomi/find"
......@@ -51,15 +54,17 @@ var Trace = false
// Method encapsulates a decoded SOAP client request
type Method struct {
Name string
This types.ManagedObjectReference
Body types.AnyType
Name string
This types.ManagedObjectReference
Header soap.Header
Body types.AnyType
}
// Service decodes incoming requests and dispatches to a Handler
type Service struct {
client *vim25.Client
sm *SessionManager
sdk map[string]*Registry
readAll func(io.Reader) ([]byte, error)
......@@ -70,7 +75,8 @@ type Service struct {
// Server provides a simulator Service over HTTP
type Server struct {
*httptest.Server
URL *url.URL
URL *url.URL
Tunnel int
caFile string
}
......@@ -80,6 +86,7 @@ func New(instance *ServiceInstance) *Service {
s := &Service{
readAll: ioutil.ReadAll,
sm: Map.SessionManager(),
sdk: make(map[string]*Registry),
}
s.client, _ = vim25.NewClient(context.Background(), s)
......@@ -110,12 +117,12 @@ func Fault(msg string, fault types.BaseMethodFault) *soap.Fault {
}
func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
handler := Map.Get(method.This)
handler := ctx.Map.Get(method.This)
session := ctx.Session
if session == nil {
switch method.Name {
case "RetrieveServiceContent", "Login", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
case "RetrieveServiceContent", "List", "Login", "LoginByToken", "LoginExtensionByCertificate", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
// ok for now, TODO: authz
default:
fault := &types.NotAuthenticated{
......@@ -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(method.Body))
Map.WithLock(handler, func() {
ctx.Map.WithLock(handler, func() {
res = m.Call(args)
})
......@@ -197,6 +204,7 @@ func (s *Service) RoundTrip(ctx context.Context, request, response soap.HasFault
}
res := s.call(&Context{
Map: Map,
Context: ctx,
Session: internalContext.Session,
}, method)
......@@ -281,6 +289,16 @@ func (s *Service) About(w http.ResponseWriter, r *http.Request) {
_ = 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
func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
......@@ -305,17 +323,19 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
res: w,
m: s.sm,
Map: s.sdk[r.URL.Path],
Context: context.Background(),
}
Map.WithLock(s.sm, ctx.mapSession)
ctx.Map.WithLock(s.sm, ctx.mapSession)
var res soap.HasFault
var soapBody interface{}
method, err := UnmarshalBody(body)
method, err := UnmarshalBody(ctx.Map.typeFunc, body)
if err != nil {
res = serverFault(err.Error())
} else {
ctx.Header = method.Header
res = s.call(ctx, method)
}
......@@ -447,15 +467,10 @@ func (*Service) ServiceVersions(w http.ResponseWriter, r *http.Request) {
// NewServer returns an http Server instance for the given service
func (s *Service) NewServer() *Server {
mux := s.ServeMux
if mux == nil {
mux = http.NewServeMux()
}
path := "/sdk"
s.RegisterSDK(Map)
mux.HandleFunc(path, s.ServeSDK)
mux.HandleFunc(path+"/vimServiceVersions.xml", s.ServiceVersions)
mux := s.ServeMux
mux.HandleFunc(Map.Path+"/vimServiceVersions.xml", s.ServiceVersions)
mux.HandleFunc(folderPrefix, s.ServeDatastore)
mux.HandleFunc("/about", s.About)
......@@ -466,7 +481,7 @@ func (s *Service) NewServer() *Server {
u := &url.URL{
Scheme: "http",
Host: ts.Listener.Addr().String(),
Path: path,
Path: Map.Path,
User: url.UserPassword("user", "pass"),
}
......@@ -478,14 +493,31 @@ func (s *Service) NewServer() *Server {
_ = f.Value.Set("")
}
cert := ""
if s.TLS == nil {
ts.Start()
} else {
ts.TLS = s.TLS
ts.TLS.ClientAuth = tls.RequestClientCert // Used by SessionManager.LoginExtensionByCertificate
ts.StartTLS()
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{
Server: ts,
URL: u,
......@@ -525,6 +557,60 @@ func (s *Server) CertificateFile() (string, error) {
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
// requests on this server have completed.
func (s *Server) Close() {
......@@ -536,7 +622,6 @@ func (s *Server) Close() {
var (
vim25MapType = types.TypeFunc()
typeFunc = defaultMapType
)
func defaultMapType(name string) (reflect.Type, bool) {
......@@ -552,14 +637,39 @@ func defaultMapType(name string) (reflect.Type, bool) {
return typ, ok
}
// UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type
func UnmarshalBody(data []byte) (*Method, error) {
body := struct {
// Element can be used to defer decoding of an XML node.
type Element struct {
start xml.StartElement
inner struct {
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{
Body: &body,
Header: &soap.Header{
Security: new(Element),
},
Body: body,
}
err := xml.Unmarshal(data, &req)
......@@ -567,40 +677,38 @@ func UnmarshalBody(data []byte) (*Method, error) {
return nil, fmt.Errorf("xml.Unmarshal: %s", err)
}
decoder := xml.NewDecoder(bytes.NewReader([]byte(body.Content)))
decoder.TypeFunc = typeFunc // required to decode interface types
var start *xml.StartElement
var start xml.StartElement
var ok bool
decoder := body.decoder()
for {
tok, derr := decoder.Token()
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 {
start = &t
if start, ok = tok.(xml.StartElement); ok {
break
}
}
kind := start.Name.Local
if !ok {
return nil, fmt.Errorf("decoding: method token not found")
}
kind := start.Name.Local
rtype, ok := typeFunc(kind)
if !ok {
return nil, fmt.Errorf("no vmomi type defined for '%s'", kind)
}
var val interface{}
if rtype != nil {
val = reflect.New(rtype).Interface()
}
val := reflect.New(rtype).Interface()
err = decoder.DecodeElement(val, start)
err = decoder.DecodeElement(val, &start)
if err != nil {
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")
......
......@@ -39,6 +39,10 @@ func (v *VirtualMachineSnapshot) RemoveSnapshotTask(req *types.RemoveSnapshot_Ta
}
vm.Snapshot.RootSnapshotList = removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren)
if len(vm.Snapshot.RootSnapshotList) == 0 {
vm.Snapshot = nil
}
})
return nil, nil
......
......@@ -17,11 +17,13 @@ limitations under the License.
package simulator
import (
"bytes"
"fmt"
"log"
"net"
"os"
"path"
"strconv"
"strings"
"sync/atomic"
"time"
......@@ -63,7 +65,7 @@ func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualM
MemoryAllocation: &rspec.MemoryAllocation,
CpuAllocation: &rspec.CpuAllocation,
}
vm.Snapshot = &types.VirtualMachineSnapshotInfo{}
vm.Snapshot = nil // intentionally set to nil until a snapshot is created
vm.Storage = &types.VirtualMachineStorageInfo{
Timestamp: time.Now(),
}
......@@ -420,6 +422,28 @@ func (vm *VirtualMachine) generateMAC() 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 {
device := spec.Device
d := device.GetVirtualDevice()
......@@ -469,6 +493,7 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec
c.MacAddress = vm.generateMAC()
}
case *types.VirtualDisk:
summary = fmt.Sprintf("%s KB", numberToString(x.CapacityInKB, ','))
switch b := d.Backing.(type) {
case types.BaseVirtualDeviceFileBackingInfo:
info := b.GetVirtualDeviceFileBackingInfo()
......@@ -991,8 +1016,7 @@ func (vm *VirtualMachine) RemoveAllSnapshotsTask(req *types.RemoveAllSnapshots_T
refs := allSnapshotsInTree(vm.Snapshot.RootSnapshotList)
vm.Snapshot.CurrentSnapshot = nil
vm.Snapshot.RootSnapshotList = nil
vm.Snapshot = nil
for _, ref := range refs {
Map.Remove(ref)
......
......@@ -58,6 +58,10 @@ var Setting = []types.BaseOptionValue{
Value: "https://127.0.0.1/sso-adminserver/sdk/vsphere.local",
},
&types.OptionValue{
Key: "VirtualCenter.InstanceName",
Value: "127.0.0.1",
},
&types.OptionValue{
Key: "event.batchsize",
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
import (
"context"
"encoding/json"
"strings"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/soap"
"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
// 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
......@@ -43,19 +57,28 @@ type Client struct {
// NewClient creates and returns a new client wirh the ServiceContent field
// filled in.
func NewClient(ctx context.Context, rt soap.RoundTripper) (*Client, error) {
serviceContent, err := methods.GetServiceContent(ctx, rt)
if err != nil {
return nil, err
}
c := Client{
ServiceContent: serviceContent,
RoundTripper: rt,
RoundTripper: rt,
}
// Set client if it happens to be a soap.Client
if sc, ok := rt.(*soap.Client); ok {
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
......
......@@ -24,14 +24,15 @@ import (
"github.com/vmware/govmomi/vim25/types"
)
var ServiceInstance = types.ManagedObjectReference{
// copy of vim25.ServiceInstance to avoid import cycle
var serviceInstance = types.ManagedObjectReference{
Type: "ServiceInstance",
Value: "ServiceInstance",
}
func GetServiceContent(ctx context.Context, r soap.RoundTripper) (types.ServiceContent, error) {
req := types.RetrieveServiceContent{
This: ServiceInstance,
This: serviceInstance,
}
res, err := RetrieveServiceContent(ctx, r, &req)
......@@ -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) {
req := types.CurrentTime{
This: ServiceInstance,
This: serviceInstance,
}
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");
you may not use this file except in compliance with the License.
......@@ -164,6 +164,22 @@ func init() {
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 {
CryptoManager
......@@ -759,9 +775,10 @@ func init() {
type HostGraphicsManager struct {
ExtensibleManagedObject
GraphicsInfo []types.HostGraphicsInfo `mo:"graphicsInfo"`
GraphicsConfig *types.HostGraphicsConfig `mo:"graphicsConfig"`
SharedPassthruGpuTypes []string `mo:"sharedPassthruGpuTypes"`
GraphicsInfo []types.HostGraphicsInfo `mo:"graphicsInfo"`
GraphicsConfig *types.HostGraphicsConfig `mo:"graphicsConfig"`
SharedPassthruGpuTypes []string `mo:"sharedPassthruGpuTypes"`
SharedGpuCapabilities []types.HostSharedGpuCapabilities `mo:"sharedGpuCapabilities"`
}
func init() {
......@@ -853,6 +870,20 @@ func init() {
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 {
Self types.ManagedObjectReference
}
......@@ -894,7 +925,10 @@ func init() {
type HostProfile struct {
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() {
......@@ -962,18 +996,25 @@ func init() {
type HostSystem struct {
ManagedEntity
Runtime types.HostRuntimeInfo `mo:"runtime"`
Summary types.HostListSummary `mo:"summary"`
Hardware *types.HostHardwareInfo `mo:"hardware"`
Capability *types.HostCapability `mo:"capability"`
LicensableResource types.HostLicensableResourceInfo `mo:"licensableResource"`
ConfigManager types.HostConfigManager `mo:"configManager"`
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"`
Runtime types.HostRuntimeInfo `mo:"runtime"`
Summary types.HostListSummary `mo:"summary"`
Hardware *types.HostHardwareInfo `mo:"hardware"`
Capability *types.HostCapability `mo:"capability"`
LicensableResource types.HostLicensableResourceInfo `mo:"licensableResource"`
RemediationState *types.HostSystemRemediationState `mo:"remediationState"`
PrecheckRemediationResult *types.ApplyHostProfileConfigurationSpec `mo:"precheckRemediationResult"`
RemediationResult *types.ApplyHostProfileConfigurationResult `mo:"remediationResult"`
ComplianceCheckState *types.HostSystemComplianceCheckState `mo:"complianceCheckState"`
ComplianceCheckResult *types.ComplianceResult `mo:"complianceCheckResult"`
ConfigManager types.HostConfigManager `mo:"configManager"`
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 {
......@@ -1056,10 +1097,13 @@ func init() {
type HttpNfcLease struct {
Self types.ManagedObjectReference
InitializeProgress int32 `mo:"initializeProgress"`
Info *types.HttpNfcLeaseInfo `mo:"info"`
State types.HttpNfcLeaseState `mo:"state"`
Error *types.LocalizedMethodFault `mo:"error"`
InitializeProgress int32 `mo:"initializeProgress"`
TransferProgress int32 `mo:"transferProgress"`
Mode string `mo:"mode"`
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 {
......
......@@ -100,11 +100,12 @@ func NewReader(ctx context.Context, s Sinker, r io.Reader, size int64) *reader {
// underlying channel.
func (r *reader) Read(b []byte) (int, error) {
n, err := r.r.Read(b)
if err != nil {
r.pos += int64(n)
if err != nil && err != io.EOF {
return n, err
}
r.pos += int64(n)
q := readerReport{
t: time.Now(),
pos: r.pos,
......
......@@ -28,6 +28,7 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/cookiejar"
......@@ -53,17 +54,9 @@ type RoundTripper interface {
}
const (
DefaultVimNamespace = "urn:vim25"
DefaultVimVersion = "6.5"
DefaultMinVimVersion = "5.5"
SessionCookieName = "vmware_soap_session"
SessionCookieName = "vmware_soap_session"
)
type header struct {
Cookie string `xml:"vcSessionCookie,omitempty"`
ID string `xml:"operationID,omitempty"`
}
type Client struct {
http.Client
......@@ -71,7 +64,6 @@ type Client struct {
k bool // Named after curl's -k flag
d *debugContainer
t *http.Transport
p *url.URL
hostsMu sync.Mutex
hosts map[string]string
......@@ -149,20 +141,33 @@ func NewClient(u *url.URL, insecure bool) *Client {
c.u = c.URL()
c.u.User = nil
c.Namespace = DefaultVimNamespace
c.Version = DefaultVimVersion
return &c
}
// NewServiceClient creates a NewClient with the given URL.Path and namespace.
func (c *Client) NewServiceClient(path string, namespace string) *Client {
u := c.URL()
u.Path = path
vc := c.URL()
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.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
client.Client.Jar.SetCookies(u, c.Client.Jar.Cookies(u))
......@@ -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
}
......@@ -346,19 +354,33 @@ func splitHostPort(host string) (string, string) {
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) {
t := c.Client.Transport.(*http.Transport)
// Extension certificate
// Extension or HoK certificate
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
host, _ := splitHostPort(c.u.Host)
host := tunnel.u.Hostname()
// Should be no reason to change the default port other than testing
key := "GOVMOMI_TUNNEL_PROXY_PORT"
port := c.URL().Query().Get(key)
port := tunnel.URL().Query().Get(key)
if port == "" {
port = os.Getenv(key)
}
......@@ -367,20 +389,14 @@ func (c *Client) SetCertificate(cert tls.Certificate) {
host += ":" + port
}
c.p = &url.URL{
t.Proxy = http.ProxyURL(&url.URL{
Scheme: "http",
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.
c.u.Host = sdkTunnel
tunnel.u.Host = sdkTunnel
return tunnel
}
func (c *Client) URL() *url.URL {
......@@ -426,21 +442,41 @@ func (c *Client) do(ctx context.Context, req *http.Request) (*http.Response, err
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 {
var err error
var b []byte
reqEnv := Envelope{Body: reqBody}
resEnv := Envelope{Body: resBody}
h := &header{
Cookie: c.cookie,
h, ok := ctx.Value(headerContext{}).(Header)
if !ok {
h = Header{}
}
// We added support for OperationID before soap.Header was exported.
if id, ok := ctx.Value(types.ID{}).(string); ok {
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
d := c.d.newRoundTrip()
......@@ -448,9 +484,16 @@ func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error
defer d.done()
}
b, err := xml.Marshal(reqEnv)
if err != nil {
panic(err)
if signer, ok := h.Security.(Signer); ok {
b, err = signer.Sign(reqEnv)
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))
......@@ -462,8 +505,13 @@ func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error
req = req.WithContext(ctx)
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 != "" {
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");
you may not use this file except in compliance with the License.
......@@ -21,9 +21,17 @@ import (
"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 {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
Header interface{} `xml:",omitempty"`
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
Header *Header `xml:"http://schemas.xmlsoap.org/soap/envelope/ Header,omitempty"`
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");
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