*: Bump version of vmware/govmomi

Bumping version to include changes that
better handle TLS errors. Bump nescessary
to prepare for when the version of Go is
bumped to 1.20

Signed-off-by: Madhav Jivrajani <madhav.jiv@gmail.com>
This commit is contained in:
Madhav Jivrajani
2023-01-02 20:56:02 +05:30
parent 45df8f0bb3
commit 8b064fa4be
263 changed files with 32336 additions and 4373 deletions

View File

@@ -26,8 +26,8 @@ var (
// NewMD5 and NewSHA1.
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
h.Reset()
h.Write(space[:])
h.Write(data)
h.Write(space[:]) //nolint:errcheck
h.Write(data) //nolint:errcheck
s := h.Sum(nil)
var uuid UUID
copy(uuid[:], s)

118
vendor/github.com/google/uuid/null.go generated vendored Normal file
View File

@@ -0,0 +1,118 @@
// Copyright 2021 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"bytes"
"database/sql/driver"
"encoding/json"
"fmt"
)
var jsonNull = []byte("null")
// NullUUID represents a UUID that may be null.
// NullUUID implements the SQL driver.Scanner interface so
// it can be used as a scan destination:
//
// var u uuid.NullUUID
// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u)
// ...
// if u.Valid {
// // use u.UUID
// } else {
// // NULL value
// }
//
type NullUUID struct {
UUID UUID
Valid bool // Valid is true if UUID is not NULL
}
// Scan implements the SQL driver.Scanner interface.
func (nu *NullUUID) Scan(value interface{}) error {
if value == nil {
nu.UUID, nu.Valid = Nil, false
return nil
}
err := nu.UUID.Scan(value)
if err != nil {
nu.Valid = false
return err
}
nu.Valid = true
return nil
}
// Value implements the driver Valuer interface.
func (nu NullUUID) Value() (driver.Value, error) {
if !nu.Valid {
return nil, nil
}
// Delegate to UUID Value function
return nu.UUID.Value()
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (nu NullUUID) MarshalBinary() ([]byte, error) {
if nu.Valid {
return nu.UUID[:], nil
}
return []byte(nil), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (nu *NullUUID) UnmarshalBinary(data []byte) error {
if len(data) != 16 {
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
}
copy(nu.UUID[:], data)
nu.Valid = true
return nil
}
// MarshalText implements encoding.TextMarshaler.
func (nu NullUUID) MarshalText() ([]byte, error) {
if nu.Valid {
return nu.UUID.MarshalText()
}
return jsonNull, nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (nu *NullUUID) UnmarshalText(data []byte) error {
id, err := ParseBytes(data)
if err != nil {
nu.Valid = false
return err
}
nu.UUID = id
nu.Valid = true
return nil
}
// MarshalJSON implements json.Marshaler.
func (nu NullUUID) MarshalJSON() ([]byte, error) {
if nu.Valid {
return json.Marshal(nu.UUID)
}
return jsonNull, nil
}
// UnmarshalJSON implements json.Unmarshaler.
func (nu *NullUUID) UnmarshalJSON(data []byte) error {
if bytes.Equal(data, jsonNull) {
*nu = NullUUID{}
return nil // valid null UUID
}
err := json.Unmarshal(data, &nu.UUID)
nu.Valid = err == nil
return err
}

View File

@@ -9,7 +9,7 @@ import (
"fmt"
)
// Scan implements sql.Scanner so UUIDs can be read from databases transparently
// Scan implements sql.Scanner so UUIDs can be read from databases transparently.
// Currently, database types that map to string and []byte are supported. Please
// consult database-specific driver documentation for matching types.
func (uuid *UUID) Scan(src interface{}) error {

View File

@@ -12,6 +12,7 @@ import (
"fmt"
"io"
"strings"
"sync"
)
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
@@ -33,7 +34,27 @@ const (
Future // Reserved for future definition.
)
var rander = rand.Reader // random function
const randPoolSize = 16 * 16
var (
rander = rand.Reader // random function
poolEnabled = false
poolMu sync.Mutex
poolPos = randPoolSize // protected with poolMu
pool [randPoolSize]byte // protected with poolMu
)
type invalidLengthError struct{ len int }
func (err invalidLengthError) Error() string {
return fmt.Sprintf("invalid UUID length: %d", err.len)
}
// IsInvalidLengthError is matcher function for custom error invalidLengthError
func IsInvalidLengthError(err error) bool {
_, ok := err.(invalidLengthError)
return ok
}
// Parse decodes s into a UUID or returns an error. Both the standard UUID
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
@@ -68,7 +89,7 @@ func Parse(s string) (UUID, error) {
}
return uuid, nil
default:
return uuid, fmt.Errorf("invalid UUID length: %d", len(s))
return uuid, invalidLengthError{len(s)}
}
// s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
@@ -112,7 +133,7 @@ func ParseBytes(b []byte) (UUID, error) {
}
return uuid, nil
default:
return uuid, fmt.Errorf("invalid UUID length: %d", len(b))
return uuid, invalidLengthError{len(b)}
}
// s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
@@ -243,3 +264,31 @@ func SetRand(r io.Reader) {
}
rander = r
}
// EnableRandPool enables internal randomness pool used for Random
// (Version 4) UUID generation. The pool contains random bytes read from
// the random number generator on demand in batches. Enabling the pool
// may improve the UUID generation throughput significantly.
//
// Since the pool is stored on the Go heap, this feature may be a bad fit
// for security sensitive applications.
//
// Both EnableRandPool and DisableRandPool are not thread-safe and should
// only be called when there is no possibility that New or any other
// UUID Version 4 generation function will be called concurrently.
func EnableRandPool() {
poolEnabled = true
}
// DisableRandPool disables the randomness pool if it was previously
// enabled with EnableRandPool.
//
// Both EnableRandPool and DisableRandPool are not thread-safe and should
// only be called when there is no possibility that New or any other
// UUID Version 4 generation function will be called concurrently.
func DisableRandPool() {
poolEnabled = false
defer poolMu.Unlock()
poolMu.Lock()
poolPos = randPoolSize
}

View File

@@ -14,11 +14,21 @@ func New() UUID {
return Must(NewRandom())
}
// NewString creates a new random UUID and returns it as a string or panics.
// NewString is equivalent to the expression
//
// uuid.New().String()
func NewString() string {
return Must(NewRandom()).String()
}
// NewRandom returns a Random (Version 4) UUID.
//
// The strength of the UUIDs is based on the strength of the crypto/rand
// package.
//
// Uses the randomness pool if it was enabled with EnableRandPool.
//
// A note about uniqueness derived from the UUID Wikipedia entry:
//
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
@@ -27,7 +37,10 @@ func New() UUID {
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
// year and having one duplicate.
func NewRandom() (UUID, error) {
return NewRandomFromReader(rander)
if !poolEnabled {
return NewRandomFromReader(rander)
}
return newRandomFromPool()
}
// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader.
@@ -41,3 +54,23 @@ func NewRandomFromReader(r io.Reader) (UUID, error) {
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil
}
func newRandomFromPool() (UUID, error) {
var uuid UUID
poolMu.Lock()
if poolPos == randPoolSize {
_, err := io.ReadFull(rander, pool[:])
if err != nil {
poolMu.Unlock()
return Nil, err
}
poolPos = 0
}
copy(uuid[:], pool[poolPos:(poolPos+16)])
poolPos += 16
poolMu.Unlock()
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil
}

2
vendor/github.com/vmware/govmomi/.dockerignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
Dockerfile*
.*ignore

View File

@@ -1,3 +1,13 @@
secrets.yml
dist/
.idea/
# ignore tools binaries
/git-chglog
# ignore RELEASE-specific CHANGELOG
/RELEASE_CHANGELOG.md
# Ignore editor temp files
*~
.vscode/

18
vendor/github.com/vmware/govmomi/.golangci.yml generated vendored Normal file
View File

@@ -0,0 +1,18 @@
linters:
disable-all: true
enable:
- goimports
- govet
# Run with --fast=false for more extensive checks
fast: true
# override defaults
linters-settings:
goimports:
# put imports beginning with prefix after 3rd-party packages;
# it's a comma-separated list of prefixes
local-prefixes: github.com/vmware/govmomi
run:
timeout: 6m
skip-dirs:
- vim25/xml
- cns/types

View File

@@ -1,57 +1,148 @@
---
project_name: govc
project_name: govmomi
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*
- id: govc
goos: &goos-defs
- linux
- darwin
- windows
- freebsd
goarch: &goarch-defs
- amd64
- arm
- arm64
- mips64le
env:
- CGO_ENABLED=0
- PKGPATH=github.com/vmware/govmomi/govc/flags
main: ./govc/main.go
binary: govc
ldflags:
- "-X {{.Env.PKGPATH}}.BuildVersion={{.Version}} -X {{.Env.PKGPATH}}.BuildCommit={{.ShortCommit}} -X {{.Env.PKGPATH}}.BuildDate={{.Date}}"
- id: vcsim
goos: *goos-defs
goarch: *goarch-defs
env:
- CGO_ENABLED=0
main: ./vcsim/main.go
binary: vcsim
ldflags:
- "-X main.buildVersion={{.Version}} -X main.buildCommit={{.ShortCommit}} -X main.buildDate={{.Date}}"
archives:
- id: govcbuild
builds:
- govc
name_template: "govc_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
replacements: &replacements
darwin: Darwin
linux: Linux
windows: Windows
freebsd: FreeBSD
amd64: x86_64
format_overrides: &overrides
- goos: windows
format: zip
files: &extrafiles
- CHANGELOG.md
- LICENSE.txt
- README.md
- id: vcsimbuild
builds:
- vcsim
name_template: "vcsim_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
replacements: *replacements
format_overrides: *overrides
files: *extrafiles
snapshot:
name_template: "{{ .Tag }}-next"
checksum:
name_template: '{{ .ProjectName }}_{{ .Version }}_checksums.txt'
name_template: "checksums.txt"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- "^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"
# upload disabled since it is maintained in homebrew-core
brews:
- name: govc
ids:
- govcbuild
tap:
owner: govmomi
name: homebrew-tap
# TODO: create token in specified tap repo, add as secret to govmomi repo and reference in release workflow
# token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
# enable once we do fully automated releases
skip_upload: true
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"
install: |
bin.install "govc"
- name: vcsim
ids:
- vcsimbuild
tap:
owner: govmomi
name: homebrew-tap
# TODO: create token in specified tap repo, add as secret to govmomi repo and reference in release workflow
# token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
# enable once we do fully automated releases
skip_upload: true
commit_author:
name: Alfred the Narwhal
email: cna-alfred@vmware.com
folder: Formula
homepage: "https://github.com/vmware/govmomi/blob/master/vcsim/README.md"
description: "vcsim is a vSphere API simulator built on top of govmomi."
test: |
system "#{bin}/vcsim -h"
install: |
bin.install "vcsim"
dockers:
- image: vmware/govc
goos: linux
goarch: amd64
binary: govc
tag_templates:
- "{{ .Tag }}"
- "v{{ .Major }}"
- "v{{ .Major }}.{{ .Minor }}"
- latest
- image_templates:
- "vmware/govc:{{ .Tag }}"
- "vmware/govc:{{ .ShortCommit }}"
- "vmware/govc:latest"
dockerfile: Dockerfile.govc
ids:
- govc
build_flag_templates:
- "--pull"
- "--label=org.opencontainers.image.created={{.Date}}"
- "--label=org.opencontainers.image.title={{.ProjectName}}"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Version}}"
- "--label=org.opencontainers.image.url=https://github.com/vmware/govmomi"
- "--platform=linux/amd64"
- image_templates:
- "vmware/vcsim:{{ .Tag }}"
- "vmware/vcsim:{{ .ShortCommit }}"
- "vmware/vcsim:latest"
dockerfile: Dockerfile.vcsim
ids:
- vcsim
build_flag_templates:
- "--pull"
- "--label=org.opencontainers.image.created={{.Date}}"
- "--label=org.opencontainers.image.title={{.ProjectName}}"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Version}}"
- "--label=org.opencontainers.image.url=https://github.com/vmware/govmomi"
- "--platform=linux/amd64"

View File

@@ -1,28 +1,45 @@
amanpaha <amanpahariya@microsoft.com> amanpaha <84718160+amanpaha@users.noreply.github.com>
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br> Amanda Hager Lopes de Andrade Katz <amanda.katz@serpro.gov.br>
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br> amandahla <amanda.andrade@serpro.gov.br>
Amit Bathla <abathla@.vmware.com> <abathla@promb-1s-dhcp216.eng.vmware.com>
Andrew Kutz <akutz@vmware.com> akutz <akutz@vmware.com>
Andrew Kutz <akutz@vmware.com> <sakutz@gmail.com>
Andrew Kutz <akutz@vmware.com> akutz <akutz@vmware.com>
Andrew Kutz <akutz@vmware.com> Andrew Kutz <101085+akutz@users.noreply.github.com>
Anfernee Yongkun Gui <agui@vmware.com> <anfernee.gui@gmail.com>
Anfernee Yongkun Gui <agui@vmware.com> Yongkun Anfernee Gui <agui@vmware.com>
Anna Carrigan <anna.carrigan@hpe.com> Anna <anna.carrigan@outlook.com>
Balu Dontu <bdontu@vmware.com> BaluDontu <bdontu@vmware.com>
Bruce Downs <bruceadowns@gmail.com> <bdowns@vmware.com>
Bruce Downs <bruceadowns@gmail.com> <bruce.downs@jivesoftware.com>
Bruce Downs <bruceadowns@gmail.com> <bruce.downs@autodesk.com>
Bruce Downs <bruceadowns@gmail.com> <bruce.downs@jivesoftware.com>
Clint Greenwood <cgreenwood@vmware.com> <clint.greenwood@gmail.com>
Cédric Blomart <cblomart@gmail.com> <cedric.blomart@minfin.fed.be>
Cédric Blomart <cblomart@gmail.com> cedric <cblomart@gmail.com>
David Stark <dave@davidstark.name> <david.stark@bskyb.com>
Doug MacEachern <dougm@vmware.com> dougm <dougm@users.noreply.github.com>
Eric Gray <egray@vmware.com> <ericgray@users.noreply.github.com>
Eric Yutao <eric.yutao@gmail.com> eric <eric.yutao@gmail.com>
Fabio Rapposelli <fabio@vmware.com> <fabio@rapposelli.org>
Faiyaz Ahmed <faiyaza@vmware.com> Faiyaz Ahmed <ahmedf@vmware.com>
Faiyaz Ahmed <faiyaza@vmware.com> Faiyaz Ahmed <faiyaza@gmail.com>
Faiyaz Ahmed <faiyaza@vmware.com> Faiyaz Ahmed <fdawg4l@users.noreply.github.com>
Henrik Hodne <henrik@travis-ci.com> <henrik@hodne.io>
Ian Eyberg <ian@deferpanic.com> <ian@opuler.com>
Jeremy Canady <jcanady@jackhenry.com> <jcanady@gmail.com>
Jiatong Wang <wjiatong@vmware.com> jiatongw <wjiatong@vmware.com>
Lintong Jiang <lintongj@vmware.com> lintongj <55512168+lintongj@users.noreply.github.com>
Michael Gasch <mgasch@vmware.com> Michael Gasch <embano1@live.com>
Mincho Tonev <mtonev@vmware.com> matonev <31008054+matonev@users.noreply.github.com>
Parveen Chahal <parkuma@microsoft.com> <mail.chahal@gmail.com>
Pieter Noordhuis <pnoordhuis@vmware.com> <pcnoordhuis@gmail.com>
Saad Malik <saad@spectrocloud.com> <simfox3@gmail.com>
Takaaki Furukawa <takaaki.frkw@gmail.com> takaaki.furukawa <takaaki.furukawa@mail.rakuten.com>
Takaaki Furukawa <takaaki.frkw@gmail.com> tkak <takaaki.frkw@gmail.com>
Vadim Egorov <vegorov@vmware.com> <egorovv@gmail.com>
Anfernee Yongkun Gui <agui@vmware.com> <anfernee.gui@gmail.com>
Anfernee Yongkun Gui <agui@vmware.com> Yongkun Anfernee Gui <agui@vmware.com>
Zach Tucker <ztucker@vmware.com> <jzt@users.noreply.github.com>
Zee Yang <zeey@vmware.com> <zee.yang@gmail.com>
Jiatong Wang <wjiatong@vmware.com> jiatongw <wjiatong@vmware.com>
Uwe Bessle <Uwe.Bessle@iteratec.de> Uwe Bessle <u.bessle.extern@eos-ts.com>
Uwe Bessle <Uwe.Bessle@iteratec.de> Uwe Bessle <uwe.bessle@web.de>
Vadim Egorov <vegorov@vmware.com> <egorovv@gmail.com>
William Lam <wlam@vmware.com> <info.virtuallyghetto@gmail.com>
Yun Zhou <yunz@vmware.com> <41678287+gh05tn0va@users.noreply.github.com>
Zach G <zguan@vmware.com> zach96guan <zach96guan@users.noreply.github.com>
Zach Tucker <ztucker@vmware.com> <jzt@users.noreply.github.com>
Zee Yang <zeey@vmware.com> <zee.yang@gmail.com>

View File

@@ -1,95 +0,0 @@
# Use the newer Travis-CI build templates based on the
# Debian Linux distribution "Trusty" release.
os: linux
dist: trusty
# Disable sudo for all builds by default. This ensures all jobs use
# Travis-CI's containerized build environment unless specified otherwise.
# The container builds have *much* shorter queue times than the VM-based
# build environment on which the sudo builds depend.
sudo: false
services: false
# Set the version of Go.
language: go
go: 1.11
# Always set the project's Go import path to ensure that forked
# builds get cloned to the correct location.
go_import_path: github.com/vmware/govmomi
# Ensure all the jobs know where the temp directory is.
env:
global: TMPDIR=/tmp
jobs:
include:
# The "lint" stage runs the various linters against the project.
- &lint-stage
stage: lint
env: LINTER=govet
install: true
script: make "${LINTER}"
- <<: *lint-stage
env: LINTER=goimports
# The "build" stage verifies the program can be built against the
# various GOOS and GOARCH combinations found in the Go releaser
# config file, ".goreleaser.yml".
- &build-stage
stage: build
env: GOOS=linux GOARCH=amd64
install: true
script: make install
- <<: *build-stage
env: GOOS=linux GOARCH=386
- <<: *build-stage
env: GOOS=darwin GOARCH=amd64
- <<: *build-stage
env: GOOS=darwin GOARCH=386
- <<: *build-stage
env: GOOS=freebsd GOARCH=amd64
- <<: *build-stage
env: GOOS=freebsd GOARCH=386
- <<: *build-stage
env: GOOS=windows GOARCH=amd64
- <<: *build-stage
env: GOOS=windows GOARCH=386
# The test stage executes the test target.
- stage: test
install: true
script: make test
# The deploy stage deploys the build artifacts using goreleaser.
#
# This stage will only be activated when there is an annotated tag present
# or when the text "/ci-deploy" is present in the commit message. However,
# the "deploy" phase of the build will still only be executed on non-PR
# builds as that restriction is baked into Travis-CI.
#
# Finally, this stage requires the Travis-CI VM infrastructure in order to
# leverage Docker. This will increase the amount of time the jobs sit
# in the queue, waiting to be built. However, it's a necessity as Travis-CI
# only allows the use of Docker with VM builds.
- stage: deploy
if: tag IS present OR commit_message =~ /\/ci-deploy/
sudo: required
services: docker
install: true
script: make install
after_success: docker login -u="${DOCKER_USERNAME}" -p="${DOCKER_PASSWORD}"
deploy:
- provider: script
skip_cleanup: true
script: curl -sL http://git.io/goreleaser | bash
addons:
apt:
update: true
packages: xmlstarlet

File diff suppressed because it is too large Load Diff

View File

@@ -1,101 +1,197 @@
# Contributing to govmomi
# Contributing to `govmomi`
## Getting started
First, fork the repository on GitHub to your personal account.
Note that _GOPATH_ can be any directory, the example below uses _$HOME/govmomi_.
Change _$USER_ below to your github username if they are not the same.
Change `$USER` in the examples below to your Github username if they are not the
same.
``` shell
export GOPATH=$HOME/govmomi
go get github.com/vmware/govmomi
cd $GOPATH/src/github.com/vmware/govmomi
git config push.default nothing # anything to avoid pushing to vmware/govmomi by default
```bash
git clone https://github.com/vmware/govmomi.git && cd govmomi
# prevent accidentally pushing to vmware/govmomi
git config push.default nothing
git remote rename origin vmware
# add your fork
git remote add $USER git@github.com:$USER/govmomi.git
git fetch $USER
git fetch -av
```
## Installing from source
Compile the govmomi libraries and install govc using:
``` shell
go install -v github.com/vmware/govmomi/govc
```
Note that **govc/build.sh** is only used for building release binaries.
## Contribution flow
## Contribution Flow
This is a rough outline of what a contributor's workflow looks like:
- Create an issue describing the feature/fix
- Create a topic branch from where you want to base your work.
- Make commits of logical units.
- Make sure your commit messages are in the proper format (see below).
- Update CHANGELOG.md and/or govc/CHANGELOG.md when appropriate.
- Push your changes to a topic branch in your fork of the repository.
- Submit a pull request to vmware/govmomi.
- Submit a pull request to `vmware/govmomi`.
Example:
See [below](#format-of-the-commit-message) for details on commit best practices
and **supported prefixes**, e.g. `govc: <message>`.
``` shell
git checkout -b my-new-feature vmware/master
git commit -a
git push $USER my-new-feature
> **Note:** If you are new to Git(hub) check out [Git rebase, squash...oh
> my!](https://www.mgasch.com/2021/05/git-basics/) for more details on how to
> successfully contribute to an open source project.
### Example 1 - Fix a Bug in `govmomi`
```bash
git checkout -b issue-<number> vmware/master
git add <files>
git commit -m "fix: ..." -m "Closes: #<issue-number>"
git push $USER issue-<number>
```
### Stay in sync with upstream
### Example 2 - Add a new (non-breaking) API to `govmomi`
When your branch gets out of sync with the vmware/master branch, use the following to update:
```bash
git checkout -b issue-<number> vmware/master
git add <files>
git commit -m "Add API ..." -m "Closes: #<issue-number>"
git push $USER issue-<number>
```
``` shell
git checkout my-new-feature
### Example 3 - Add a Feature to `govc`
```bash
git checkout -b issue-<number> vmware/master
git add <files>
git commit -m "govc: Add feature ..." -m "Closes: #<issue-number>"
git push $USER issue-<number>
```
**Note**:
To register the new `govc` command package, add a blank `_` import to `govmomi/govc/main.go`.
### Example 4 - Fix a Bug in `vcsim`
```bash
git checkout -b issue-<number> vmware/master
git add <files>
git commit -m "vcsim: Fix ..." -m "Closes: #<issue-number>"
git push $USER issue-<number>
```
### Example 5 - Document Breaking (API) Changes
Breaking changes, e.g. to the `govmomi` APIs, are highlighted in the `CHANGELOG`
and release notes when the keyword `BREAKING:` is used in the commit message
body.
The text after `BREAKING:` is used in the corresponding highlighted section.
Thus these details should be stated at the body of the commit message.
Multi-line strings are supported.
```bash
git checkout -b issue-<number> vmware/master
git add <files>
cat << EOF | git commit -F -
Add ctx to funcXYZ
This commit introduces context.Context to function XYZ
Closes: #1234
BREAKING: Add ctx to funcXYZ()
EOF
git push $USER issue-<number>
```
### Stay in sync with Upstream
When your branch gets out of sync with the vmware/master branch, use the
following to update (rebase):
```bash
git checkout issue-<number>
git fetch -a
git rebase vmware/master
git push --force-with-lease $USER my-new-feature
git push --force-with-lease $USER issue-<number>
```
### Updating pull requests
### Updating Pull Requests
If your PR fails to pass CI or needs changes based on code review, you'll most likely want to squash these changes into
existing commits.
If your PR fails to pass CI or needs changes based on code review, it's ok to
add more commits stating the changes made, e.g. "Address review comments". This
is to assist the reviewer(s) to easily detect and review the recent changes.
If your pull request contains a single commit or your changes are related to the most recent commit, you can simply
amend the commit.
In case of small PRs, it's ok to squash and force-push (see further below)
directly instead.
``` shell
```bash
# incorporate review feedback
git add .
git commit --amend
git push --force-with-lease $USER my-new-feature
```
If you need to squash changes into an earlier commit, you can use:
``` shell
git add .
# create a fixup commit which will be merged into your (original) <commit>
git commit --fixup <commit>
git rebase -i --autosquash vmware/master
git push --force-with-lease $USER my-new-feature
git push $USER issue-<number>
```
Be sure to add a comment to the PR indicating your new changes are ready to review, as github does not generate a
notification when you git push.
Be sure to add a comment to the PR indicating your new changes are ready to
review, as Github does not generate a notification when you git push.
### Code style
Once the review is complete, squash and push your final commit(s):
The coding style suggested by the Golang community is used in govmomi. See the
```bash
# squash all commits into one
# --autosquash will automatically detect and merge fixup commits
git rebase -i --autosquash vmware/master
git push --force-with-lease $USER issue-<number>
```
### Code Style
The coding style suggested by the Go community is used in `govmomi`. See the
[style doc](https://github.com/golang/go/wiki/CodeReviewComments) for details.
Try to limit column width to 120 characters for both code and markdown documents such as this one.
Try to limit column width to 120 characters for both code and markdown documents
such as this one.
### Format of the Commit Message
We follow the conventions on [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/).
We follow the conventions described in [How to Write a Git Commit
Message](http://chris.beams.io/posts/git-commit/).
Be sure to include any related GitHub issue references in the commit message.
Be sure to include any related GitHub issue references in the commit message,
e.g. `Closes: #<number>`.
The [`CHANGELOG.md`](./CHANGELOG.md) and release page uses **commit message
prefixes** for grouping and highlighting. A commit message that
starts with `[prefix:] ` will place this commit under the respective
section in the `CHANGELOG`.
The following example creates a commit referencing the `issue: 1234` and puts
the commit message in the `govc` `CHANGELOG` section:
```bash
git commit -s -m "govc: Add CLI command X" -m "Closes: #1234"
```
Currently the following prefixes are used:
- `api:` - Use for API-related changes
- `govc:` - Use for changes to `govc` CLI
- `vcsim:` - Use for changes to vCenter Simulator
- `chore:` - Use for repository related activities
- `fix:` - Use for bug fixes
- `docs:` - Use for changes to the documentation
- `examples:` - Use for changes to examples
If your contribution falls into multiple categories, e.g. `api` and `vcsim` it
is recommended to break up your commits using distinct prefixes.
### Running CI Checks and Tests
You can run both `make check` and `make test` from the top level of the
repository.
While `make check` will catch formatting and import errors, it will not apply
any fixes. The developer is expected to do that.
## Reporting Bugs and Creating Issues
When opening a new issue, try to roughly follow the commit message format conventions above.
When opening a new issue, try to roughly follow the commit message format
conventions above.

View File

@@ -5,104 +5,252 @@
Abhijeet Kasurde <akasurde@redhat.com>
abrarshivani <abrarshivani@users.noreply.github.com>
Adam Chalkley <atc0005@users.noreply.github.com>
Adam Fowler <adam@adamfowler.org>
Adam Shannon <adamkshannon@gmail.com>
Akanksha Panse <pansea@vmware.com>
Al Biheiri <abiheiri@apple.com>
Alessandro Cortiana <alessandro.cortiana@gmail.com>
Alex <puzo2002@gmail.com>
Alex Bozhenko <alexbozhenko@fb.com>
Alex Ellis (VMware) <alexellis2@gmail.com>
Aligator <8278538+yet-another-aligator@users.noreply.github.com>
Alvaro Miranda <kikitux@gmail.com>
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br>
amanpaha <amanpahariya@microsoft.com>
Amit Bathla <abathla@.vmware.com>
amit bezalel <amit.bezalel@hpe.com>
Andrew <AndrewDi@users.noreply.github.com>
Andrew Chin <andrew@andrewtchin.com>
Andrew Kutz <akutz@vmware.com>
Andrey Klimentyev <andrey.klimentyev@flant.com>
Anfernee Yongkun Gui <agui@vmware.com>
angystardust <angystardust@users.noreply.github.com>
aniketGslab <aniket.shinde@gslab.com>
Ankit Vaidya <vaidyaa@vmware.com>
Ankur Huralikoppi <huralikoppia@vmware.com>
Anna Carrigan <anna.carrigan@hpe.com>
Antony Saba <awsaba@gmail.com>
Ariel Chinn <arielchinn@gmail.com>
Arran Walker <arran.walker@zopa.com>
Artem Anisimov <aanisimov@inbox.ru>
Arunesh Pandey <parunesh@vmware.com>
Aryeh Weinreb <aryehweinreb@gmail.com>
Augy StClair <augy@google.com>
Austin Parker <aparker@apprenda.com>
Balu Dontu <bdontu@vmware.com>
bastienbc <bastien.barbe.creuly@gmail.com>
Ben Corrie <bcorrie@vmware.com>
Ben Vickers <bvickers@pivotal.io>
Benjamin Davini <davinib@vmware.com>
Benjamin Peterson <benjamin@python.org>
Benjamin Vickers <bvickers@vmware.com>
Bhavya Choudhary <bhavyac@vmware.com>
Bob Killen <killen.bob@gmail.com>
Brad Fitzpatrick <bradfitz@golang.org>
Brian Rak <brak@vmware.com>
brian57860 <brian57860@users.noreply.github.com>
Bruce Downs <bruceadowns@gmail.com>
Bryan Venteicher <bryanventeicher@gmail.com>
Cédric Blomart <cblomart@gmail.com>
Cheng Cheng <chengch@vmware.com>
Chethan Venkatesh <chethanv@vmware.com>
Choudhury Sarada Prasanna Nanda <cspn@google.com>
Chris Marchesi <chrism@vancluevertech.com>
Christian Höltje <docwhat@gerf.org>
Clint Greenwood <cgreenwood@vmware.com>
cpiment <pimentel.carlos@gmail.com>
CuiHaozhi <cuihaozhi@chinacloud.com.cn>
Dan Ilan <danilan@google.com>
Dan Norris <protochron@users.noreply.github.com>
Daniel Frederick Crisman <daniel@crisman.org>
Daniel Mueller <deso@posteo.net>
Danny Lockard <danny.lockard@banno.com>
Dave Gress <gressd@vmware.com>
Dave Smith-Uchida <dsmithuchida@vmware.com>
Dave Tucker <dave@dtucker.co.uk>
Davide Agnello <dagnello@hp.com>
David Gress <gressd@vmware.com>
David Stark <dave@davidstark.name>
Davide Agnello <dagnello@hp.com>
Davinder Kumar <davinderk@vmware.com>
Defa <zhoudefa666@163.com>
demarey <christophe.demarey@inria.fr>
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Deric Crago <deric.crago@gmail.com>
ditsuke <ditsuke@protonmail.com>
Divyen Patel <divyenp@vmware.com>
Dnyanesh Gate <dnyanesh.gate@druva.com>
Doug MacEachern <dougm@vmware.com>
East <60801291+houfangdong@users.noreply.github.com>
Eloy Coto <eloy.coto@gmail.com>
embano1 <embano1@users.noreply.github.com>
Eng Zer Jun <engzerjun@gmail.com>
Eric Edens <ericedens@google.com>
Eric Graham <16710890+Pheric@users.noreply.github.com>
Eric Gray <egray@vmware.com>
Eric Yutao <eric.yutao@gmail.com>
Erik Hollensbe <github@hollensbe.org>
Essodjolo KAHANAM <essodjolo@kahanam.com>
Ethan Kaley <ethan.kaley@emc.com>
Evan Chu <echu@vmware.com>
Fabio Rapposelli <fabio@vmware.com>
Faiyaz Ahmed <ahmedf@vmware.com>
Faiyaz Ahmed <faiyaza@vmware.com>
Federico Pellegatta <12744504+federico-pellegatta@users.noreply.github.com>
forkbomber <forkbomber@users.noreply.github.com>
François Rigault <rigault.francois@gmail.com>
freebsdly <qinhuajun@outlook.com>
Gavin Gray <gavin@infinio.com>
Gavrie Philipson <gavrie.philipson@elastifile.com>
George Hicken <ghicken@vmware.com>
Gerrit Renker <Gerrit.Renker@ctl.io>
gthombare <gthombare@vmware.com>
HakanSunay <hakansunay@abv.bg>
Hasan Mahmood <mahmoodh@vmware.com>
Haydon Ryan <haydon.ryan@gmail.com>
Heiko Reese <hreese@users.noreply.github.com>
Henrik Hodne <henrik@travis-ci.com>
hkumar <hkumar@vmware.com>
Hrabur Stoyanov <hstoyanov@vmware.com>
hui luo <luoh@vmware.com>
Ian Eyberg <ian@deferpanic.com>
Isaac Rodman <isaac@eyz.us>
Ivan Mikushin <imikushin@vmware.com>
Ivan Porto Carrero <icarrero@vmware.com>
James King <james.king@emc.com>
James Peach <jpeach@vmware.com>
Jason Kincl <jkincl@gmail.com>
Jeremy Canady <jcanady@jackhenry.com>
jeremy-clerc <jeremy@clerc.io>
Jiatong Wang <wjiatong@vmware.com>
jingyizPensando <jingyiz@pensando.io>
João Pereira <joaodrp@gmail.com>
Jonas Ausevicius <jonas.ausevicius@virtustream.com>
Jorge Sevilla <jorge.sevilla@rstor.io>
Julien PILLON <jpillon@lesalternatives.org>
Justin J. Novack <jnovack@users.noreply.github.com>
kayrus <kay.diam@gmail.com>
Keenan Brock <keenan@thebrocks.net>
Kevin George <georgek@vmware.com>
Knappek <andy.knapp.ak@gmail.com>
Leslie Wang <qiwa@pensando.io>
leslie-qiwa <leslie.qiwa@gmail.com>
Lintong Jiang <lintongj@vmware.com>
Liping Xue <lipingx@vmware.com>
Louie Jiang <jiangl@vmware.com>
Luther Monson <luther.monson@gmail.com>
Madanagopal Arunachalam <marunachalam@vmware.com>
makelarisjr <8687447+makelarisjr@users.noreply.github.com>
maplain <fangyuanl@vmware.com>
Marc Carmier <mcarmier@gmail.com>
Marcus Tan <marcus.tan@rubrik.com>
Maria Ntalla <maria.ntalla@gmail.com>
Marin Atanasov Nikolov <mnikolov@vmware.com>
Mario Trangoni <mjtrangoni@gmail.com>
Mark Dechiaro <mdechiaro@users.noreply.github.com>
Mark Peek <markpeek@vmware.com>
Mark Rexwinkel <Mark.Rexwinkel@elekta.com>
martin <martin@catai.org>
Matt Clay <matt@mystile.com>
Matthew Cosgrove <matthew.cosgrove@dell.com>
Matt Moore <mattmoor@vmware.com>
Matt Moriarity <matt@mattmoriarity.com>
Matthew Cosgrove <matthew.cosgrove@dell.com>
mbhadale <mbhadale@vmware.com>
Merlijn Sebrechts <merlijn.sebrechts@gmail.com>
Mevan Samaratunga <mevansam@gmail.com>
Michael Gasch <15986659+embano1@users.noreply.github.com>
Michael Gasch <mgasch@vmware.com>
Michal Jankowski <mjankowski@vmware.com>
Mike Schinkel <mike@newclarity.net>
Mincho Tonev <mtonev@vmware.com>
mingwei <mingwei@smartx.com>
Nicolas Lamirault <nicolas.lamirault@gmail.com>
Nikhil Kathare <nikhil.kathare@netapp.com>
Nikhil R Deshpande <ndeshpande@vmware.com>
Nikolas Grottendieck <git@nikolasgrottendieck.com>
Nils Elde <nils.elde@sscinc.com>
nirbhay <nirbhay.bagmar@nutanix.com>
Nobuhiro MIKI <nmiki@yahoo-corp.jp>
Om Kumar <om.kumar@hpe.com>
Omar Kohl <omarkohl@gmail.com>
Parham Alvani <parham.alvani@gmail.com>
Parveen Chahal <parkuma@microsoft.com>
Paul Martin <25058109+rawstorage@users.noreply.github.com>
Pierre Gronlier <pierre.gronlier@corp.ovh.com>
Pieter Noordhuis <pnoordhuis@vmware.com>
pradeepj <50135054+pradeep288@users.noreply.github.com>
Pranshu Jain <jpranshu@vmware.com>
prydin <prydin@vmware.com>
rconde01 <rconde01@hotmail.com>
rHermes <teodor_spaeren@riseup.net>
Rianto Wahyudi <rwahyudi@gmail.com>
Ricardo Katz <rkatz@vmware.com>
Robin Watkins <robwatkins@gmail.com>
Rowan Jacobs <rojacobs@pivotal.io>
Roy Ling <royling0024@gmail.com>
rsikdar <rsikdar@berkeley.edu>
runner.mei <runner.mei@gmail.com>
Ryan Johnson <johnsonryan@vmware.com>
S R Ashrith <sashrith@vmware.com>
S.Çağlar Onur <conur@vmware.com>
Saad Malik <saad@spectrocloud.com>
Sam Zhu <zhusa@zhusa-a02.vmware.com>
samzhu333 <45263849+samzhu333@users.noreply.github.com>
Sandeep Pissay Srinivasa Rao <ssrinivas@vmware.com>
Scott Holden <scott@nullops.io>
Sergey Ignatov <sergey.ignatov@jetbrains.com>
serokles <timbo.alexander@gmail.com>
shahra <shahra@vmware.com>
Shalini Bhaskara <sbhaskara@vmware.com>
Shaozhen Ding <dsz0111@gmail.com>
Shawn Neal <sneal@sneal.net>
shylasrinivas <sshyla@vmware.com>
sky-joker <sky.jokerxx@gmail.com>
smaftoul <samuel.maftoul@gmail.com>
smahadik <smahadik@vmware.com>
Sten Feldman <exile@chamber.ee>
Stepan Mazurov <smazurov@gmail.com>
Steve Purcell <steve@sanityinc.com>
Sudhindra Aithal <sudhiaithal@pensando.io>
SUMIT AGRAWAL <asumit@vmware.com>
Sunny Carter <sunny.carter@metaswitch.com>
syuparn <s.hello.spagetti@gmail.com>
Takaaki Furukawa <takaaki.frkw@gmail.com>
Tamas Eger <tamas.eger@bitrise.io>
Tanay Kothari <tkothari@vmware.com>
tanishi <tanishi503@gmail.com>
Ted Zlatanov <tzz@lifelogs.com>
Thad Craft <tcraft@pivotal.io>
Thibaut Ackermann <thibaut.ackermann@alcatel-lucent.com>
Tim McNamara <tim.mcnamara@canonical.com>
Tjeu Kayim <15987676+TjeuKayim@users.noreply.github.com>
Toomas Pelberg <toomas.pelberg@playtech.com>
Trevor Dawe <trevor.dawe@gmail.com>
tshihad <tshihad9@gmail.com>
Uwe Bessle <Uwe.Bessle@iteratec.de>
Vadim Egorov <vegorov@vmware.com>
Vikram Krishnamurthy <vikramkrishnamu@vmware.com>
volanja <volaaanja@gmail.com>
Volodymyr Bobyr <pupsua@gmail.com>
Waldek Maleska <w.maleska@gmail.com>
William Lam <wlam@vmware.com>
Witold Krecicki <wpk@culm.net>
xing-yang <xingyang105@gmail.com>
xinyanw409 <wxinyan@vmware.com>
Yang Yang <yangy@vmware.com>
yangxi <yangxi@vmware.com>
Yann Hodique <yhodique@google.com>
Yash Nitin Desai <desaiy@vmware.com>
Yassine TIJANI <ytijani@vmware.com>
Yi Jiang <yijiang@vmware.com>
yiyingy <yiyingy@vmware.com>
ykakarap <yuva2811@gmail.com>
Yogesh Sobale <6104071+ysobale@users.noreply.github.com>
Yue Yin <yueyin@yuyin-a01.vmware.com>
Yun Zhou <yunz@vmware.com>
Yuya Kusakabe <yuya.kusakabe@gmail.com>
Zacharias Taubert <zacharias.taubert@gmail.com>
Zach G <zguan@vmware.com>
Zach Tucker <ztucker@vmware.com>
Zacharias Taubert <zacharias.taubert@gmail.com>
Zee Yang <zeey@vmware.com>
zyuxin <zyuxin@vmware.com>
Кузаков Евгений <kuzakov@satel.org>

View File

@@ -1,4 +0,0 @@
FROM scratch
LABEL maintainer="fabio@vmware.com"
COPY govc /
ENTRYPOINT [ "/govc" ]

45
vendor/github.com/vmware/govmomi/Dockerfile.govc generated vendored Normal file
View File

@@ -0,0 +1,45 @@
# Create a builder container
# golang:1.18.0-buster amd64
FROM golang@sha256:7d39537344486528f8cdb3bd8adb98ab7f0f4236044b6944fed8631da35a4ce5 AS build
WORKDIR /go/src/app
# Create appuser to isolate potential vulnerabilities
# See https://stackoverflow.com/a/55757473/12429735
ENV USER=appuser
ENV UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
"${USER}"
# Create a new tmp directory so no bad actors can manipulate it
RUN mkdir /temporary-tmp-directory && chmod 777 /temporary-tmp-directory
###############################################################################
# Final stage
FROM scratch
# Allow container to use latest TLS certificates
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# Copy over appuser to run as non-root
COPY --from=build /etc/passwd /etc/passwd
COPY --from=build /etc/group /etc/group
# Copy over the /tmp directory for golang/os.TmpDir
COPY --chown=appuser --from=build /temporary-tmp-directory /tmp
# Copy application from external build
COPY govc /govc
# Run all commands as non-root
USER appuser:appuser
# session cache, etc
ENV GOVMOMI_HOME=/tmp
# Set CMD to application with container defaults
CMD ["/govc"]

47
vendor/github.com/vmware/govmomi/Dockerfile.vcsim generated vendored Normal file
View File

@@ -0,0 +1,47 @@
# Create a builder container
# golang:1.18.0-buster amd64
FROM golang@sha256:7d39537344486528f8cdb3bd8adb98ab7f0f4236044b6944fed8631da35a4ce5 AS build
WORKDIR /go/src/app
# Create appuser to isolate potential vulnerabilities
# See https://stackoverflow.com/a/55757473/12429735
ENV USER=appuser
ENV UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
"${USER}"
# Create a new tmp directory so no bad actors can manipulate it
RUN mkdir /temporary-tmp-directory && chmod 777 /temporary-tmp-directory
###############################################################################
# Final stage
FROM scratch
# Run all commands as non-root
USER appuser:appuser
# Allow container to use latest TLS certificates
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# Copy over appuser to run as non-root
COPY --from=build /etc/passwd /etc/passwd
COPY --from=build /etc/group /etc/group
# Copy over the /tmp directory for golang/os.TmpDir
COPY --chown=appuser --from=build /temporary-tmp-directory /tmp
# Expose application port
EXPOSE 8989
# Copy application from external build
COPY vcsim /vcsim
# Set entrypoint to application with container defaults
ENTRYPOINT [ "/vcsim" ]
CMD ["-l", "0.0.0.0:8989"]

View File

@@ -1,60 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "improvements"
digest = "1:b183578c34fabccaf65f1a57d2efeec2086abdce1446978d69ab3a0016cb750c"
name = "github.com/davecgh/go-xdr"
packages = ["xdr2"]
pruneopts = "NUT"
revision = "4930550ba2e22f87187498acfd78348b15f4e7a8"
source = "https://github.com/rasky/go-xdr"
[[projects]]
digest = "1:1ab18cf8c2084968d6dca0dd46fbda9efba08664ecd7957b63c7ca57bb2455df"
name = "github.com/google/uuid"
packages = ["."]
pruneopts = "NUT"
revision = "6a5e28554805e78ea6141142aba763936c4761c0"
[[projects]]
branch = "govmomi"
digest = "1:f49ed6cb2129e9a3ce9dde5037cb243b5849c0ec0c7973b9d1e987872d8b8cc6"
name = "github.com/kr/pretty"
packages = ["."]
pruneopts = "NUT"
revision = "2ee9d7453c02ef7fa518a83ae23644eb8872186a"
source = "https://github.com/dougm/pretty"
[[projects]]
branch = "master"
digest = "1:c3a7836b5904db0f8b609595b619916a6831cb35b8b714aec39f96d00c6155d8"
name = "github.com/kr/text"
packages = ["."]
pruneopts = "NUT"
revision = "7cafcd837844e784b526369c9bce262804aebc60"
[[projects]]
branch = "master"
digest = "1:4bea31865971675c482ed875caeabe7d2182dcb47d52900b7da5236d66dc9970"
name = "github.com/vmware/vmw-guestinfo"
packages = [
"bdoor",
"message",
"vmcheck",
]
pruneopts = "NUT"
revision = "25eff159a728be87e103a0b8045e08273f4dbec4"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/davecgh/go-xdr/xdr2",
"github.com/google/uuid",
"github.com/kr/pretty",
"github.com/vmware/vmw-guestinfo/message",
"github.com/vmware/vmw-guestinfo/vmcheck",
]
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -1,19 +0,0 @@
# 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"

View File

@@ -1,29 +1,158 @@
.PHONY: test
# Copyright (c) 2021 VMware, Inc. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
all: check test
# If you update this file, please follow
# https://www.thapaliya.com/en/writings/well-documented-makefiles/
check: goimports govet
# Ensure Make is run with bash shell as some syntax below is bash-specific
SHELL := /usr/bin/env bash
goimports:
@echo checking go imports...
@command -v goimports >/dev/null 2>&1 || go get golang.org/x/tools/cmd/goimports
@! goimports -d . 2>&1 | egrep -v '^$$'
# Print the help/usage when make is executed without any other arguments
.DEFAULT_GOAL := help
govet:
@echo checking go vet...
@go tool vet -structtags=false -methods=false $$(find . -mindepth 1 -maxdepth 1 -type d -not -name vendor)
install:
## --------------------------------------
## Help
## --------------------------------------
.PHONY: help
help: ## Display usage
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make [target] \033[36m\033[0m\n\nTargets:\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
## --------------------------------------
## Locations and programs
## --------------------------------------
# Directories
BIN_DIR := bin
TOOLS_DIR := hack/tools
TOOLS_BIN_DIR := $(TOOLS_DIR)/bin
# Tooling binaries
GO ?= $(shell command -v go 2>/dev/null)
GOLANGCI_LINT := $(TOOLS_BIN_DIR)/golangci-lint
## --------------------------------------
## Prerequisites
## --------------------------------------
# Do not proceed unless the go binary is present.
ifeq (,$(strip $(GO)))
$(error The "go" program cannot be found)
endif
## --------------------------------------
## Linting and fixing linter errors
## --------------------------------------
.PHONY: lint
lint: ## Run all the lint targets
$(MAKE) lint-go-full
GOLANGCI_LINT_FLAGS ?= --fast=true
.PHONY: lint-go
lint-go: $(GOLANGCI_LINT) ## Lint codebase
$(GOLANGCI_LINT) run -v $(GOLANGCI_LINT_FLAGS)
.PHONY: lint-go-full
lint-go-full: GOLANGCI_LINT_FLAGS = --fast=false
lint-go-full: lint-go ## Run slower linters to detect possible issues
.PHONY: fix
fix: GOLANGCI_LINT_FLAGS = --fast=false --fix
fix: lint-go ## Tries to fix errors reported by lint-go-full target
.PHONY: check
check: lint-go-full
check: ## Run linters
## --------------------------------------
## Tooling Binaries
## --------------------------------------
TOOLING_BINARIES := $(GOLANGCI_LINT)
tools: $(TOOLING_BINARIES) ## Build tooling binaries
.PHONY: $(TOOLING_BINARIES)
$(TOOLING_BINARIES):
cd $(TOOLS_DIR); make $(@F)
## --------------------------------------
## Build / Install
## --------------------------------------
.PHONY: install
install: ## Install govc and vcsim
$(MAKE) -C govc install
$(MAKE) -C vcsim install
go-test:
GORACE=history_size=5 go test -timeout 5m -count 1 -race -v $(TEST_OPTS) ./...
## --------------------------------------
## Generate
## --------------------------------------
.PHONY: mod
mod: ## Runs go mod tidy to validate modules
go mod tidy -v
.PHONY: mod-get
mod-get: ## Downloads and caches the modules
go mod download
.PHONY: doc
doc: install
doc: ## Generates govc USAGE.md
./govc/usage.sh > ./govc/USAGE.md
## --------------------------------------
## Tests
## --------------------------------------
# Test options
TEST_COUNT ?= 1
TEST_TIMEOUT ?= 5m
TEST_RACE_HISTORY_SIZE ?= 5
GORACE ?= history_size=$(TEST_RACE_HISTORY_SIZE)
ifeq (-count,$(findstring -count,$(TEST_OPTS)))
$(error Use TEST_COUNT to override this option)
endif
ifeq (-race,$(findstring -race,$(TEST_OPTS)))
$(error The -race flag is enabled by default & cannot be specified in TEST_OPTS)
endif
ifeq (-timeout,$(findstring -timeout,$(TEST_OPTS)))
$(error Use TEST_TIMEOUT to override this option)
endif
.PHONY: go-test
go-test: ## Runs go unit tests with race detector enabled
GORACE=$(GORACE) $(GO) test \
-count $(TEST_COUNT) \
-race \
-timeout $(TEST_TIMEOUT) \
-v $(TEST_OPTS) \
./...
.PHONY: govc-test
govc-test: install
govc-test: ## Runs govc bats tests
./govc/test/images/update.sh
(cd govc/test && ./vendor/github.com/sstephenson/bats/libexec/bats -t .)
test: go-test govc-test
.PHONY: govc-test-sso
govc-test-sso: install
./govc/test/images/update.sh
(cd govc/test && SSO_BATS=1 ./vendor/github.com/sstephenson/bats/libexec/bats -t sso.bats)
doc: install
./govc/usage.sh > ./govc/USAGE.md
.PHONY: govc-test-sso-assert-cert
govc-test-sso-assert-cert:
SSO_BATS_ASSERT_CERT=1 $(MAKE) govc-test-sso
.PHONY: test
test: go-test govc-test ## Runs go-test and govc-test

View File

@@ -1,94 +1,131 @@
[![Build Status](https://travis-ci.org/vmware/govmomi.png?branch=master)](https://travis-ci.org/vmware/govmomi)
[![Go Report Card](https://goreportcard.com/badge/github.com/vmware/govmomi)](https://goreportcard.com/report/github.com/vmware/govmomi)
<!-- markdownlint-disable first-line-h1 no-inline-html -->
[![Build](https://github.com/vmware/govmomi/actions/workflows/govmomi-build.yaml/badge.svg)][ci-build]
[![Tests](https://github.com/vmware/govmomi/actions/workflows/govmomi-go-tests.yaml/badge.svg)][ci-tests]
[![Go Report Card](https://goreportcard.com/badge/github.com/vmware/govmomi)][go-report-card]
[![Latest Release](https://img.shields.io/github/release/vmware/govmomi.svg?logo=github&style=flat-square)][latest-release]
[![Go Reference](https://pkg.go.dev/badge/github.com/vmware/govmomi.svg)][go-reference]
[![go.mod Go version](https://img.shields.io/github/go-mod/go-version/vmware/govmomi)][go-version]
# govmomi
A Go library for interacting with VMware vSphere APIs (ESXi and/or vCenter).
A Go library for interacting with VMware vSphere APIs (ESXi and/or vCenter Server).
In addition to the vSphere API client, this repository includes:
* [govc](./govc) - vSphere CLI
* [vcsim](./vcsim) - vSphere API mock framework
* [toolbox](./toolbox) - VM guest tools framework
* [govc][govc] - vSphere CLI
* [vcsim][vcsim] - vSphere API mock framework
* [toolbox][toolbox] - VM guest tools framework
## Compatibility
This library is built for and tested against ESXi and vCenter 6.0, 6.5 and 6.7.
This library supports vCenter Server and ESXi versions following the [VMware Product Lifecycle Matrix][reference-lifecycle].
It may work with versions 5.5 and 5.1, but neither are officially supported.
Product versions that are end of support may work, but are not officially supported.
## Documentation
The APIs exposed by this library very closely follow the API described in the [VMware vSphere API Reference Documentation][apiref].
Refer to this document to become familiar with the upstream API.
The APIs exposed by this library closely follow the API described in the [VMware vSphere API Reference Documentation][reference-api]. Refer to the documentation to become familiar with the upstream API.
The code in the `govmomi` package is a wrapper for the code that is generated from the vSphere API description.
It primarily provides convenience functions for working with the vSphere API.
See [godoc.org][godoc] for documentation.
[apiref]:http://pubs.vmware.com/vsphere-6-5/index.jsp#com.vmware.wssdk.apiref.doc/right-pane.html
[godoc]:http://godoc.org/github.com/vmware/govmomi
The code in the `govmomi` package is a wrapper for the code that is generated from the vSphere API description. It primarily provides convenience functions for working with the vSphere API. See [godoc.org][reference-godoc] for documentation.
## Installation
```sh
### govmomi (Package)
```bash
go get -u github.com/vmware/govmomi
```
### Binaries and Docker Images for `govc` and `vcsim`
Installation instructions, released binaries, and Docker images are documented in the respective README files of [`govc`][govc] and [`vcsim`][vcsim].
## Discussion
Contributors and users are encouraged to collaborate using GitHub issues and/or
[Slack](https://vmwarecode.slack.com/messages/govmomi).
Access to Slack requires a [VMware {code} membership](https://code.vmware.com/join/).
The project encourages the community to collaborate using GitHub [issues][govmomi-github-issues], GitHub [discussions][govmomi-github-discussions], and [Slack][slack-channel].
> **Note**
> Access to Slack requires a free [VMware {code}][slack-join] developer program membership.
## Status
Changes to the API are subject to [semantic versioning](http://semver.org).
Changes to the API are subject to [semantic versioning][reference-semver].
Refer to the [CHANGELOG](CHANGELOG.md) for version to version changes.
Refer to the [CHANGELOG][govmomi-changelog] for version to version changes.
## Projects using govmomi
## Notable Projects Using govmomi
* [Docker Machine](https://github.com/docker/machine/tree/master/drivers/vmwarevsphere)
* [collectd-vsphere][project-travisci-collectd-vsphere]
* [Docker LinuxKit][project-docker-linuxKit]
* [Elastic Agent VMware vSphere integration][project-elastic-agent]
* [Gru][project-gru]
* [Juju][project-juju]
* [Jupiter Brain][project-travisci-jupiter-brain]
* [Kubernetes vSphere Cloud Provider][project-k8s-cloud-provider]
* [Kubernetes Cluster API][project-k8s-cluster-api]
* [OPS][project-nanovms-ops]
* [Packer Plugin for VMware vSphere][project-hashicorp-packer-plugin-vsphere]
* [Rancher][project-rancher]
* [Terraform Provider for VMware vSphere][project-hashicorp-terraform-provider-vsphere]
* [Telegraf][project-influxdata-telegraf]
* [VMware Event Broker Appliance][project-vmware-veba]
* [VMware vSphere Integrated Containers Engine][project-vmware-vic]
* [VMware vSphere 7.0][project-vmware-vsphere]
* [Docker InfraKit](https://github.com/docker/infrakit/tree/master/pkg/provider/vsphere)
## Related Projects
* [Docker LinuxKit](https://github.com/linuxkit/linuxkit/tree/master/src/cmd/linuxkit)
* [Kubernetes](https://github.com/kubernetes/kubernetes/tree/master/pkg/cloudprovider/providers/vsphere)
* [Kubernetes Cloud Provider](https://github.com/kubernetes/cloud-provider-vsphere)
* [Kubernetes Cluster API](https://github.com/kubernetes-sigs/cluster-api-provider-vsphere)
* [Kubernetes kops](https://github.com/kubernetes/kops/tree/master/upup/pkg/fi/cloudup/vsphere)
* [Terraform](https://github.com/terraform-providers/terraform-provider-vsphere)
* [Packer](https://github.com/jetbrains-infra/packer-builder-vsphere)
* [VMware VIC Engine](https://github.com/vmware/vic)
* [Travis CI](https://github.com/travis-ci/jupiter-brain)
* [collectd-vsphere](https://github.com/travis-ci/collectd-vsphere)
* [Gru](https://github.com/dnaeon/gru)
* [Libretto](https://github.com/apcera/libretto/tree/master/virtualmachine/vsphere)
* [Telegraf](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/vsphere)
* [Open Storage](https://github.com/libopenstorage/openstorage/tree/master/pkg/storageops/vsphere)
## Related projects
* [rbvmomi](https://github.com/vmware/rbvmomi)
* [pyvmomi](https://github.com/vmware/pyvmomi)
* [go-vmware-nsxt][reference-go-vmware-nsxt]
* [pyvmomi][reference-pyvmomi]
* [rbvmomi][reference-rbvmomi]
## License
govmomi is available under the [Apache 2 license](LICENSE.txt).
govmomi is available under the [Apache 2 License][govmomi-license].
## Name
Pronounced: _go·mom·ie_
Follows pyvmomi and rbvmomi: language prefix + the vSphere acronym "VM Object Management Infrastructure".
[//]: Links
[ci-build]: https://github.com/vmware/govmomi/actions/workflows/govmomi-build.yaml
[ci-tests]: https://github.com/vmware/govmomi/actions/workflows/govmomi-go-tests.yaml
[latest-release]: https://github.com/vmware/govmomi/releases/latest
[govc]: govc/README.md
[govmomi-github-issues]: https://github.com/vmware/govmomi/issues
[govmomi-github-discussions]: https://github.com/vmware/govmomi/discussions
[govmomi-changelog]: CHANGELOG.md
[govmomi-license]: LICENSE.txt
[go-reference]: https://pkg.go.dev/github.com/vmware/govmomi
[go-report-card]: https://goreportcard.com/report/github.com/vmware/govmomi
[go-version]: https://github.com/vmware/govmomi
[project-docker-linuxKit]: https://github.com/linuxkit/linuxkit/tree/master/src/cmd/linuxkit
[project-elastic-agent]: https://github.com/elastic/integrations/tree/main/packages/vsphere
[project-gru]: https://github.com/dnaeon/gru
[project-hashicorp-packer-plugin-vsphere]: https://github.com/hashicorp/packer-plugin-vsphere
[project-hashicorp-terraform-provider-vsphere]: https://github.com/hashicorp/terraform-provider-vsphere
[project-influxdata-telegraf]: https://github.com/influxdata/telegraf/tree/master/plugins/inputs/vsphere
[project-juju]: https://github.com/juju/juju
[project-k8s-cloud-provider]: https://github.com/kubernetes/cloud-provider-vsphere
[project-k8s-cluster-api]: https://github.com/kubernetes-sigs/cluster-api-provider-vsphere
[project-nanovms-ops]: https://github.com/nanovms/ops
[project-rancher]: https://github.com/rancher/rancher/blob/master/pkg/api/norman/customization/vsphere/listers.go
[project-travisci-collectd-vsphere]: https://github.com/travis-ci/collectd-vsphere
[project-travisci-jupiter-brain]: https://github.com/travis-ci/jupiter-brain
[project-vmware-veba]: https://github.com/vmware-samples/vcenter-event-broker-appliance/tree/development/vmware-event-router
[project-vmware-vic]: https://github.com/vmware/vic
[project-vmware-vsphere]: https://docs.vmware.com/en/VMware-vSphere/7.0/rn/vsphere-esxi-vcenter-server-7-vsphere-with-kubernetes-release-notes.html
[reference-api]: https://developer.vmware.com/apis/968/vsphere
[reference-godoc]: http://godoc.org/github.com/vmware/govmomi
[reference-go-vmware-nsxt]: https://github.com/vmware/go-vmware-nsxt
[reference-lifecycle]: https://lifecycle.vmware.com
[reference-pyvmomi]: https://github.com/vmware/pyvmomi
[reference-rbvmomi]: https://github.com/vmware/rbvmomi
[reference-semver]: http://semver.org
[slack-join]: https://developer.vmware.com/join/
[slack-channel]: https://vmwarecode.slack.com/messages/govmomi
[toolbox]: toolbox/README.md
[vcsim]: vcsim/README.md

225
vendor/github.com/vmware/govmomi/RELEASE.md generated vendored Normal file
View File

@@ -0,0 +1,225 @@
# How to create a `govmomi` Release on Github
> **Note**
>
> The steps outlined in this document can only be performed by maintainers or
> administrators of this project.
The release automation is based on Github
[Actions](https://github.com/features/actions) and has been improved over time
to simplify the experience for creating `govmomi` releases.
The Github Actions release [workflow](.github/workflows/govmomi-release.yaml)
uses [`goreleaser`](http://goreleaser.com/) and automatically creates/pushes:
- Release artifacts for `govc` and `vcsim` to the
[release](https://github.com/vmware/govmomi/releases) page, including
`LICENSE.txt`, `README` and `CHANGELOG`
- Docker images for `vmware/govc` and `vmware/vcsim` to Docker Hub
- Source code
Starting with release tag `v0.29.0`, releases are not tagged on the `master`
branch anymore but a dedicated release branch, for example `release-0.29`. This
process has already been followed for patch releases and back-ports.
> **Warning**
>
> If you create a release after the `v0.29.0` tag, start
> [here](#creating-a-release-after-v0290). To create a release with an older
> tag, e.g. cherrypick or back-port, continue
> [here](#creating-a-release-before-v0290).
## Creating a release after Version `v0.29.0`
The release process from `v0.29.0` has been further simplified and is done
through the Github UI. The only pre-requirement is creating a release branch,
which can be done through the Github UI or `git` CLI.
This guide describes the CLI process.
### Verify `master` branch is up to date with the remote
```console
git checkout master
git fetch -avp
git diff master origin/master
# if your local and remote branches diverge run
git pull origin/master
```
> **Warning**
>
> These steps assume `origin` to point to the remote
> `https://github.com/vmware/govmomi`, respectively
> `git@github.com:vmware/govmomi`.
### Create a release branch
For new releases, create a release branch from the most recent commit in
`master`, e.g. `release-0.30`.
```console
export RELEASE_BRANCH=release-0.30
git checkout -b ${RELEASE_BRANCH}
```
For maintenance/patch releases on **existing** release branches **after** tag
`v0.29.0` simply checkout the existing release branch and add commits to the
existing release branch.
### Verify `make docs` and `CONTRIBUTORS` are up to date
> **Warning**
>
> Run the following commands and commit any changes to the release branch before
> proceeding with the release.
```console
make doc
./scripts/contributors.sh
if [ -z "$(git status --porcelain)" ]; then
echo "working directory clean: proceed with release"
else
echo "working directory dirty: please commit changes"
fi
# perform git add && git commit ... in case there were changes
```
### Push the release branch
> **Warning**
>
> Do not create a tag as this will be done by the release automation.
The final step is pushing the new/updated release branch.
```console
git push origin ${RELEASE_BRANCH}
```
### Create a release in the Github UI
Open the `govmomi` Github [repository](https://github.com/vmware/govmomi) and
navigate to `Actions -> Workflows -> Release`.
Click `Run Workflow` which opens a dropdown list.
Select the new/updated branch, e.g. `release-0.30`, i.e. **not** the `master`
branch.
Specify a semantic `tag` to associate with the release, e.g. `v0.30.0`.
> **Warning**
>
> This tag **must not** exist or the release will fail during the validation
> phase.
By default, a dry-run is performed to rule out most (but not all) errors during
a release. If you do not want to perform a dry-run, e.g. to finally create a
release, deselect the `Verify release workflow ...` checkbox.
Click `Run Workflow` to kick off the workflow.
After successful completion and if the newly created `tag` is the **latest**
(semantic version sorted) tag in the repository, a PR is automatically opened
against the `master` branch to update the `CHANGELOG`. Please review and merge
accordingly.
## Creating a release before Version `v0.29.0`
The release process before `v0.29.0` differs since it's based on manually
creating and pushing tags. Here, on every new tag matching `v*` pushed to the
repository a Github Action Release Workflow is executed.
### Verify `master` branch is up to date with the remote
```console
git checkout master
git fetch -avp
git diff master origin/master
# if your local and remote branches diverge run
git pull origin/master
```
> **Warning**
>
> These steps assume `origin` to point to the remote
> `https://github.com/vmware/govmomi`, respectively
> `git@github.com:vmware/govmomi`.
### Create a release branch
Pick a reference (commit, branch or tag) **before** the `v0.29.0` tag and create
a release branch from there.
The following example creates a cherrypick release (`v0.28.1`) based on the
`v0.28.0` tag.
```console
export RELEASE_BRANCH=release-0.28
git checkout -b ${RELEASE_BRANCH} v0.28.0
```
Optionally, incorporate (cherry-pick) commits into the branch.
> **Warning**
>
> Make sure that these commits/ranges do not contain commits after the `v0.29.0`
> tag which include release automation changes, i.e. files in `.github/workflows/`!
### Verify `make docs` and `CONTRIBUTORS` are up to date
> **Warning**
>
> Run the following commands and commit any changes to the release branch before
> proceeding with the release.
```console
make doc
./scripts/contributors.sh
if [ -z "$(git status --porcelain)" ]; then
echo "working directory clean: proceed with release"
else
echo "working directory dirty: please commit changes"
fi
# perform git add && git commit ... in case there were changes
```
### Set `RELEASE_VERSION` variable
This variable is used and referenced in the subsequent commands. Set it to the
**upcoming** release version, adhering to the [semantic
versioning](https://semver.org/) scheme:
```console
export RELEASE_VERSION=v0.28.1
```
### Create the Git Tag
```console
git tag -a ${RELEASE_VERSION} -m "Release ${RELEASE_VERSION}"
```
### Push the new Tag
```console
# Will trigger Github Actions Release Workflow
git push --atomic origin ${RELEASE_BRANCH} refs/tags/${RELEASE_VERSION}
```
### Verify Github Action Release Workflow
After pushing a new release tag, the status of the workflow can be inspected
[here](https://github.com/vmware/govmomi/actions/workflows/govmomi-release.yaml).
![Release](static/release-workflow.png "Successful Release Run")
After a successful release, a pull request is automatically created by the
Github Actions bot to update the [CHANGELOG](CHANGELOG.md). This `CHANGELOG.md`
is also generated with `git-chglog` but uses a slightly different template
(`.chglog/CHANGELOG.tpl.md`) for rendering (issue/PR refs are excluded).

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2014-2020 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -22,9 +22,11 @@ import (
"path"
"strings"
"github.com/vmware/govmomi/internal"
"github.com/vmware/govmomi/list"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/view"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
@@ -38,16 +40,26 @@ type Finder struct {
folders *object.DatacenterFolders
}
func NewFinder(client *vim25.Client, all bool) *Finder {
func NewFinder(client *vim25.Client, all ...bool) *Finder {
props := false
if len(all) == 1 {
props = all[0]
}
f := &Finder{
client: client,
si: object.NewSearchIndex(client),
r: recurser{
Collector: property.DefaultCollector(client),
All: all,
All: props,
},
}
if len(all) == 0 {
// attempt to avoid SetDatacenter() requirement
f.dc, _ = f.DefaultDatacenter(context.Background())
}
return f
}
@@ -57,6 +69,18 @@ func (f *Finder) SetDatacenter(dc *object.Datacenter) *Finder {
return f
}
// InventoryPath composes the given object's inventory path.
// There is no vSphere property or method that provides an inventory path directly.
// This method uses the ManagedEntity.Parent field to determine the ancestry tree of the object and
// the ManagedEntity.Name field for each ancestor to compose the path.
func InventoryPath(ctx context.Context, client *vim25.Client, obj types.ManagedObjectReference) (string, error) {
entities, err := mo.Ancestors(ctx, client, client.ServiceContent.PropertyCollector, obj)
if err != nil {
return "", err
}
return internal.InventoryPath(entities), nil
}
// findRoot makes it possible to use "find" mode with a different root path.
// Example: ResourcePoolList("/dc1/host/cluster1/...")
func (f *Finder) findRoot(ctx context.Context, root *list.Element, parts []string) bool {
@@ -93,8 +117,8 @@ func (f *Finder) find(ctx context.Context, arg string, s *spec) ([]list.Element,
isPath := strings.Contains(arg, "/")
root := list.Element{
Path: "/",
Object: object.NewRootFolder(f.client),
Path: "/",
}
parts := list.ToParts(arg)
@@ -109,19 +133,10 @@ func (f *Finder) find(ctx context.Context, arg string, s *spec) ([]list.Element,
return nil, err
}
mes, err := mo.Ancestors(ctx, f.client, f.client.ServiceContent.PropertyCollector, pivot.Reference())
root.Path, err = InventoryPath(ctx, f.client, pivot.Reference())
if err != nil {
return nil, err
}
for _, me := range mes {
// Skip root entity in building inventory path.
if me.Parent == nil {
continue
}
root.Path = path.Join(root.Path, me.Name)
}
root.Object = pivot
parts = parts[1:]
}
@@ -253,7 +268,7 @@ func (f *Finder) managedObjectList(ctx context.Context, path string, tl bool, in
fn = f.dcReference
}
if len(path) == 0 {
if path == "" {
path = "."
}
@@ -271,8 +286,7 @@ func (f *Finder) managedObjectList(ctx context.Context, path string, tl bool, in
return f.find(ctx, path, s)
}
// Element returns an Element for the given ManagedObjectReference
// This method is only useful for looking up the InventoryPath of a ManagedObjectReference.
// Element is deprecated, use InventoryPath() instead.
func (f *Finder) Element(ctx context.Context, ref types.ManagedObjectReference) (*list.Element, error) {
rl := func(_ context.Context) (object.Reference, error) {
return ref, nil
@@ -301,7 +315,7 @@ func (f *Finder) Element(ctx context.Context, ref types.ManagedObjectReference)
// ObjectReference converts the given ManagedObjectReference to a type from the object package via object.NewReference
// with the object.Common.InventoryPath field set.
func (f *Finder) ObjectReference(ctx context.Context, ref types.ManagedObjectReference) (object.Reference, error) {
e, err := f.Element(ctx, ref)
path, err := InventoryPath(ctx, f.client, ref)
if err != nil {
return nil, err
}
@@ -312,7 +326,7 @@ func (f *Finder) ObjectReference(ctx context.Context, ref types.ManagedObjectRef
SetInventoryPath(string)
}
r.(common).SetInventoryPath(e.Path)
r.(common).SetInventoryPath(path)
if f.dc != nil {
if ds, ok := r.(*object.Datastore); ok {
@@ -776,9 +790,26 @@ func (f *Finder) NetworkList(ctx context.Context, path string) ([]object.Network
return ns, nil
}
// Network finds a NetworkReference using a Name, Inventory Path, ManagedObject ID, Logical Switch UUID or Segment ID.
// With standard vSphere networking, Portgroups cannot have the same name within the same network folder.
// With NSX, Portgroups can have the same name, even within the same Switch. In this case, using an inventory path
// results in a MultipleFoundError. A MOID, switch UUID or segment ID can be used instead, as both are unique.
// See also: https://kb.vmware.com/s/article/79872#Duplicate_names
// Examples:
// - Name: "dvpg-1"
// - Inventory Path: "vds-1/dvpg-1"
// - ManagedObject ID: "DistributedVirtualPortgroup:dvportgroup-53"
// - Logical Switch UUID: "da2a59b8-2450-4cb2-b5cc-79c4c1d2144c"
// - Segment ID: "/infra/segments/vnet_ce50e69b-1784-4a14-9206-ffd7f1f146f7"
func (f *Finder) Network(ctx context.Context, path string) (object.NetworkReference, error) {
networks, err := f.NetworkList(ctx, path)
if err != nil {
if _, ok := err.(*NotFoundError); ok {
net, nerr := f.networkByID(ctx, path)
if nerr == nil {
return net, nil
}
}
return nil, err
}
@@ -789,6 +820,41 @@ func (f *Finder) Network(ctx context.Context, path string) (object.NetworkRefere
return networks[0], nil
}
func (f *Finder) networkByID(ctx context.Context, path string) (object.NetworkReference, error) {
if ref := object.ReferenceFromString(path); ref != nil {
// This is a MOID
return object.NewReference(f.client, *ref).(object.NetworkReference), nil
}
kind := []string{"DistributedVirtualPortgroup"}
m := view.NewManager(f.client)
v, err := m.CreateContainerView(ctx, f.client.ServiceContent.RootFolder, kind, true)
if err != nil {
return nil, err
}
defer v.Destroy(ctx)
filter := property.Filter{
"config.logicalSwitchUuid": path,
"config.segmentId": path,
}
refs, err := v.FindAny(ctx, kind, filter)
if err != nil {
return nil, err
}
if len(refs) == 0 {
return nil, &NotFoundError{"network", path}
}
if len(refs) > 1 {
return nil, &MultipleFoundError{"network", path}
}
return object.NewReference(f.client, refs[0]).(object.NetworkReference), nil
}
func (f *Finder) DefaultNetwork(ctx context.Context) (object.NetworkReference, error) {
network, err := f.Network(ctx, "*")
if err != nil {

91
vendor/github.com/vmware/govmomi/history/collector.go generated vendored Normal file
View File

@@ -0,0 +1,91 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package history
import (
"context"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
)
type Collector struct {
r types.ManagedObjectReference
c *vim25.Client
}
func NewCollector(c *vim25.Client, ref types.ManagedObjectReference) *Collector {
return &Collector{
r: ref,
c: c,
}
}
// Reference returns the managed object reference of this collector
func (c Collector) Reference() types.ManagedObjectReference {
return c.r
}
// Client returns the vim25 client used by this collector
func (c Collector) Client() *vim25.Client {
return c.c
}
// Properties wraps property.DefaultCollector().RetrieveOne() and returns
// properties for the specified managed object reference
func (c Collector) Properties(ctx context.Context, r types.ManagedObjectReference, ps []string, dst interface{}) error {
return property.DefaultCollector(c.c).RetrieveOne(ctx, r, ps, dst)
}
func (c Collector) Destroy(ctx context.Context) error {
req := types.DestroyCollector{
This: c.r,
}
_, err := methods.DestroyCollector(ctx, c.c, &req)
return err
}
func (c Collector) Reset(ctx context.Context) error {
req := types.ResetCollector{
This: c.r,
}
_, err := methods.ResetCollector(ctx, c.c, &req)
return err
}
func (c Collector) Rewind(ctx context.Context) error {
req := types.RewindCollector{
This: c.r,
}
_, err := methods.RewindCollector(ctx, c.c, &req)
return err
}
func (c Collector) SetPageSize(ctx context.Context, maxCount int32) error {
req := types.SetCollectorPageSize{
This: c.r,
MaxCount: maxCount,
}
_, err := methods.SetCollectorPageSize(ctx, c.c, &req)
return err
}

63
vendor/github.com/vmware/govmomi/internal/helpers.go generated vendored Normal file
View File

@@ -0,0 +1,63 @@
/*
Copyright (c) 2020 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 internal
import (
"net"
"path"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
// InventoryPath composed of entities by Name
func InventoryPath(entities []mo.ManagedEntity) string {
val := "/"
for _, entity := range entities {
// Skip root folder in building inventory path.
if entity.Parent == nil {
continue
}
val = path.Join(val, entity.Name)
}
return val
}
func HostSystemManagementIPs(config []types.VirtualNicManagerNetConfig) []net.IP {
var ips []net.IP
for _, nc := range config {
if nc.NicType != string(types.HostVirtualNicManagerNicTypeManagement) {
continue
}
for ix := range nc.CandidateVnic {
for _, selectedVnicKey := range nc.SelectedVnic {
if nc.CandidateVnic[ix].Key != selectedVnicKey {
continue
}
ip := net.ParseIP(nc.CandidateVnic[ix].Spec.Ip.IpAddress)
if ip != nil {
ips = append(ips, ip)
}
}
}
}
return ips
}

123
vendor/github.com/vmware/govmomi/internal/methods.go generated vendored Normal file
View File

@@ -0,0 +1,123 @@
/*
Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package internal
import (
"context"
"github.com/vmware/govmomi/vim25/soap"
)
type RetrieveDynamicTypeManagerBody struct {
Req *RetrieveDynamicTypeManagerRequest `xml:"urn:vim25 RetrieveDynamicTypeManager"`
Res *RetrieveDynamicTypeManagerResponse `xml:"urn:vim25 RetrieveDynamicTypeManagerResponse"`
Fault_ *soap.Fault
}
func (b *RetrieveDynamicTypeManagerBody) Fault() *soap.Fault { return b.Fault_ }
func RetrieveDynamicTypeManager(ctx context.Context, r soap.RoundTripper, req *RetrieveDynamicTypeManagerRequest) (*RetrieveDynamicTypeManagerResponse, error) {
var reqBody, resBody RetrieveDynamicTypeManagerBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type RetrieveManagedMethodExecuterBody struct {
Req *RetrieveManagedMethodExecuterRequest `xml:"urn:vim25 RetrieveManagedMethodExecuter"`
Res *RetrieveManagedMethodExecuterResponse `xml:"urn:vim25 RetrieveManagedMethodExecuterResponse"`
Fault_ *soap.Fault
}
func (b *RetrieveManagedMethodExecuterBody) Fault() *soap.Fault { return b.Fault_ }
func RetrieveManagedMethodExecuter(ctx context.Context, r soap.RoundTripper, req *RetrieveManagedMethodExecuterRequest) (*RetrieveManagedMethodExecuterResponse, error) {
var reqBody, resBody RetrieveManagedMethodExecuterBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type DynamicTypeMgrQueryMoInstancesBody struct {
Req *DynamicTypeMgrQueryMoInstancesRequest `xml:"urn:vim25 DynamicTypeMgrQueryMoInstances"`
Res *DynamicTypeMgrQueryMoInstancesResponse `xml:"urn:vim25 DynamicTypeMgrQueryMoInstancesResponse"`
Fault_ *soap.Fault
}
func (b *DynamicTypeMgrQueryMoInstancesBody) Fault() *soap.Fault { return b.Fault_ }
func DynamicTypeMgrQueryMoInstances(ctx context.Context, r soap.RoundTripper, req *DynamicTypeMgrQueryMoInstancesRequest) (*DynamicTypeMgrQueryMoInstancesResponse, error) {
var reqBody, resBody DynamicTypeMgrQueryMoInstancesBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type DynamicTypeMgrQueryTypeInfoBody struct {
Req *DynamicTypeMgrQueryTypeInfoRequest `xml:"urn:vim25 DynamicTypeMgrQueryTypeInfo"`
Res *DynamicTypeMgrQueryTypeInfoResponse `xml:"urn:vim25 DynamicTypeMgrQueryTypeInfoResponse"`
Fault_ *soap.Fault
}
func (b *DynamicTypeMgrQueryTypeInfoBody) Fault() *soap.Fault { return b.Fault_ }
func DynamicTypeMgrQueryTypeInfo(ctx context.Context, r soap.RoundTripper, req *DynamicTypeMgrQueryTypeInfoRequest) (*DynamicTypeMgrQueryTypeInfoResponse, error) {
var reqBody, resBody DynamicTypeMgrQueryTypeInfoBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type ExecuteSoapBody struct {
Req *ExecuteSoapRequest `xml:"urn:vim25 ExecuteSoap"`
Res *ExecuteSoapResponse `xml:"urn:vim25 ExecuteSoapResponse"`
Fault_ *soap.Fault
}
func (b *ExecuteSoapBody) Fault() *soap.Fault { return b.Fault_ }
func ExecuteSoap(ctx context.Context, r soap.RoundTripper, req *ExecuteSoapRequest) (*ExecuteSoapResponse, error) {
var reqBody, resBody ExecuteSoapBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}

270
vendor/github.com/vmware/govmomi/internal/types.go generated vendored Normal file
View File

@@ -0,0 +1,270 @@
/*
Copyright (c) 2014 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 internal
import (
"reflect"
"github.com/vmware/govmomi/vim25/types"
)
type DynamicTypeMgrQueryMoInstancesRequest struct {
This types.ManagedObjectReference `xml:"_this"`
FilterSpec BaseDynamicTypeMgrFilterSpec `xml:"filterSpec,omitempty,typeattr"`
}
type DynamicTypeMgrQueryMoInstancesResponse struct {
Returnval []DynamicTypeMgrMoInstance `xml:"urn:vim25 returnval"`
}
type DynamicTypeEnumTypeInfo struct {
types.DynamicData
Name string `xml:"name"`
WsdlName string `xml:"wsdlName"`
Version string `xml:"version"`
Value []string `xml:"value,omitempty"`
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
}
func init() {
types.Add("DynamicTypeEnumTypeInfo", reflect.TypeOf((*DynamicTypeEnumTypeInfo)(nil)).Elem())
}
type DynamicTypeMgrAllTypeInfoRequest struct {
types.DynamicData
ManagedTypeInfo []DynamicTypeMgrManagedTypeInfo `xml:"managedTypeInfo,omitempty"`
EnumTypeInfo []DynamicTypeEnumTypeInfo `xml:"enumTypeInfo,omitempty"`
DataTypeInfo []DynamicTypeMgrDataTypeInfo `xml:"dataTypeInfo,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrAllTypeInfo", reflect.TypeOf((*DynamicTypeMgrAllTypeInfoRequest)(nil)).Elem())
}
type DynamicTypeMgrAnnotation struct {
types.DynamicData
Name string `xml:"name"`
Parameter []string `xml:"parameter,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrAnnotation", reflect.TypeOf((*DynamicTypeMgrAnnotation)(nil)).Elem())
}
type DynamicTypeMgrDataTypeInfo struct {
types.DynamicData
Name string `xml:"name"`
WsdlName string `xml:"wsdlName"`
Version string `xml:"version"`
Base []string `xml:"base,omitempty"`
Property []DynamicTypeMgrPropertyTypeInfo `xml:"property,omitempty"`
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrDataTypeInfo", reflect.TypeOf((*DynamicTypeMgrDataTypeInfo)(nil)).Elem())
}
func (b *DynamicTypeMgrFilterSpec) GetDynamicTypeMgrFilterSpec() *DynamicTypeMgrFilterSpec { return b }
type BaseDynamicTypeMgrFilterSpec interface {
GetDynamicTypeMgrFilterSpec() *DynamicTypeMgrFilterSpec
}
type DynamicTypeMgrFilterSpec struct {
types.DynamicData
}
func init() {
types.Add("DynamicTypeMgrFilterSpec", reflect.TypeOf((*DynamicTypeMgrFilterSpec)(nil)).Elem())
}
type DynamicTypeMgrManagedTypeInfo struct {
types.DynamicData
Name string `xml:"name"`
WsdlName string `xml:"wsdlName"`
Version string `xml:"version"`
Base []string `xml:"base,omitempty"`
Property []DynamicTypeMgrPropertyTypeInfo `xml:"property,omitempty"`
Method []DynamicTypeMgrMethodTypeInfo `xml:"method,omitempty"`
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrManagedTypeInfo", reflect.TypeOf((*DynamicTypeMgrManagedTypeInfo)(nil)).Elem())
}
type DynamicTypeMgrMethodTypeInfo struct {
types.DynamicData
Name string `xml:"name"`
WsdlName string `xml:"wsdlName"`
Version string `xml:"version"`
ParamTypeInfo []DynamicTypeMgrParamTypeInfo `xml:"paramTypeInfo,omitempty"`
ReturnTypeInfo *DynamicTypeMgrParamTypeInfo `xml:"returnTypeInfo,omitempty"`
Fault []string `xml:"fault,omitempty"`
PrivId string `xml:"privId,omitempty"`
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrMethodTypeInfo", reflect.TypeOf((*DynamicTypeMgrMethodTypeInfo)(nil)).Elem())
}
type DynamicTypeMgrMoFilterSpec struct {
DynamicTypeMgrFilterSpec
Id string `xml:"id,omitempty"`
TypeSubstr string `xml:"typeSubstr,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrMoFilterSpec", reflect.TypeOf((*DynamicTypeMgrMoFilterSpec)(nil)).Elem())
}
type DynamicTypeMgrMoInstance struct {
types.DynamicData
Id string `xml:"id"`
MoType string `xml:"moType"`
}
func init() {
types.Add("DynamicTypeMgrMoInstance", reflect.TypeOf((*DynamicTypeMgrMoInstance)(nil)).Elem())
}
type DynamicTypeMgrParamTypeInfo struct {
types.DynamicData
Name string `xml:"name"`
Version string `xml:"version"`
Type string `xml:"type"`
PrivId string `xml:"privId,omitempty"`
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrParamTypeInfo", reflect.TypeOf((*DynamicTypeMgrParamTypeInfo)(nil)).Elem())
}
type DynamicTypeMgrPropertyTypeInfo struct {
types.DynamicData
Name string `xml:"name"`
Version string `xml:"version"`
Type string `xml:"type"`
PrivId string `xml:"privId,omitempty"`
MsgIdFormat string `xml:"msgIdFormat,omitempty"`
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
}
type DynamicTypeMgrQueryTypeInfoRequest struct {
This types.ManagedObjectReference `xml:"_this"`
FilterSpec BaseDynamicTypeMgrFilterSpec `xml:"filterSpec,omitempty,typeattr"`
}
type DynamicTypeMgrQueryTypeInfoResponse struct {
Returnval DynamicTypeMgrAllTypeInfoRequest `xml:"urn:vim25 returnval"`
}
func init() {
types.Add("DynamicTypeMgrPropertyTypeInfo", reflect.TypeOf((*DynamicTypeMgrPropertyTypeInfo)(nil)).Elem())
}
type DynamicTypeMgrTypeFilterSpec struct {
DynamicTypeMgrFilterSpec
TypeSubstr string `xml:"typeSubstr,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrTypeFilterSpec", reflect.TypeOf((*DynamicTypeMgrTypeFilterSpec)(nil)).Elem())
}
type ReflectManagedMethodExecuterSoapArgument struct {
types.DynamicData
Name string `xml:"name"`
Val string `xml:"val"`
}
func init() {
types.Add("ReflectManagedMethodExecuterSoapArgument", reflect.TypeOf((*ReflectManagedMethodExecuterSoapArgument)(nil)).Elem())
}
type ReflectManagedMethodExecuterSoapFault struct {
types.DynamicData
FaultMsg string `xml:"faultMsg"`
FaultDetail string `xml:"faultDetail,omitempty"`
}
func init() {
types.Add("ReflectManagedMethodExecuterSoapFault", reflect.TypeOf((*ReflectManagedMethodExecuterSoapFault)(nil)).Elem())
}
type ReflectManagedMethodExecuterSoapResult struct {
types.DynamicData
Response string `xml:"response,omitempty"`
Fault *ReflectManagedMethodExecuterSoapFault `xml:"fault,omitempty"`
}
type RetrieveDynamicTypeManagerRequest struct {
This types.ManagedObjectReference `xml:"_this"`
}
type RetrieveDynamicTypeManagerResponse struct {
Returnval *InternalDynamicTypeManager `xml:"urn:vim25 returnval"`
}
type RetrieveManagedMethodExecuterRequest struct {
This types.ManagedObjectReference `xml:"_this"`
}
func init() {
types.Add("RetrieveManagedMethodExecuter", reflect.TypeOf((*RetrieveManagedMethodExecuterRequest)(nil)).Elem())
}
type RetrieveManagedMethodExecuterResponse struct {
Returnval *ReflectManagedMethodExecuter `xml:"urn:vim25 returnval"`
}
type InternalDynamicTypeManager struct {
types.ManagedObjectReference
}
type ReflectManagedMethodExecuter struct {
types.ManagedObjectReference
}
type ExecuteSoapRequest struct {
This types.ManagedObjectReference `xml:"_this"`
Moid string `xml:"moid"`
Version string `xml:"version"`
Method string `xml:"method"`
Argument []ReflectManagedMethodExecuterSoapArgument `xml:"argument,omitempty"`
}
type ExecuteSoapResponse struct {
Returnval *ReflectManagedMethodExecuterSoapResult `xml:"urn:vim25 returnval"`
}

View File

@@ -0,0 +1,25 @@
/*
Copyright (c) 2014-2022 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 version
const (
// ClientName is the name of this SDK
ClientName = "govmomi"
// ClientVersion is the version of this SDK
ClientVersion = "0.30.0"
)

View File

@@ -165,6 +165,8 @@ func (l Lister) List(ctx context.Context) ([]Element, error) {
return l.ListHostSystem(ctx)
case "VirtualApp":
return l.ListVirtualApp(ctx)
case "VmwareDistributedVirtualSwitch", "DistributedVirtualSwitch":
return l.ListDistributedVirtualSwitch(ctx)
default:
return nil, fmt.Errorf("cannot traverse type " + l.Reference.Type)
}
@@ -497,6 +499,69 @@ func (l Lister) ListHostSystem(ctx context.Context) ([]Element, error) {
return es, nil
}
func (l Lister) ListDistributedVirtualSwitch(ctx context.Context) ([]Element, error) {
ospec := types.ObjectSpec{
Obj: l.Reference,
Skip: types.NewBool(true),
}
fields := []string{
"portgroup",
}
for _, f := range fields {
tspec := types.TraversalSpec{
Path: f,
Skip: types.NewBool(false),
Type: "DistributedVirtualSwitch",
}
ospec.SelectSet = append(ospec.SelectSet, &tspec)
}
childTypes := []string{
"DistributedVirtualPortgroup",
}
var pspecs []types.PropertySpec
for _, t := range childTypes {
pspec := types.PropertySpec{
Type: t,
}
if l.All {
pspec.All = types.NewBool(true)
} else {
pspec.PathSet = []string{"name"}
}
pspecs = append(pspecs, pspec)
}
req := types.RetrieveProperties{
SpecSet: []types.PropertyFilterSpec{
{
ObjectSet: []types.ObjectSpec{ospec},
PropSet: pspecs,
},
},
}
var dst []interface{}
err := l.retrieveProperties(ctx, req, &dst)
if err != nil {
return nil, err
}
es := []Element{}
for _, v := range dst {
es = append(es, ToElement(v.(mo.Reference), l.Prefix))
}
return es, nil
}
func (l Lister) ListVirtualApp(ctx context.Context) ([]Element, error) {
ospec := types.ObjectSpec{
Obj: l.Reference,

View File

@@ -25,6 +25,7 @@ import (
"github.com/vmware/govmomi/lookup/methods"
"github.com/vmware/govmomi/lookup/types"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/soap"
vim "github.com/vmware/govmomi/vim25/types"
@@ -47,12 +48,28 @@ var (
type Client struct {
*soap.Client
RoundTripper soap.RoundTripper
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)
// PSC may be external, attempt to derive from sts.uri
path := &url.URL{Path: Path}
if c.ServiceContent.Setting != nil {
m := object.NewOptionManager(c, *c.ServiceContent.Setting)
opts, err := m.Query(ctx, "config.vpxd.sso.sts.uri")
if err == nil && len(opts) == 1 {
u, err := url.Parse(opts[0].GetOptionValue().Value.(string))
if err == nil {
path.Scheme = u.Scheme
path.Host = u.Host
}
}
}
sc := c.Client.NewServiceClient(path.String(), Namespace)
sc.Version = Version
req := types.RetrieveServiceContent{
@@ -64,7 +81,12 @@ func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
return nil, err
}
return &Client{sc, res.Returnval}, nil
return &Client{sc, sc, res.Returnval}, nil
}
// RoundTrip dispatches to the RoundTripper field.
func (c *Client) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
return c.RoundTripper.RoundTrip(ctx, req, res)
}
func (c *Client) List(ctx context.Context, filter *types.LookupServiceRegistrationFilter) ([]types.LookupServiceRegistrationInfo, error) {

View File

@@ -18,6 +18,7 @@ package simulator
import (
"github.com/google/uuid"
"github.com/vmware/govmomi/lookup"
"github.com/vmware/govmomi/lookup/types"
"github.com/vmware/govmomi/simulator"
@@ -33,6 +34,7 @@ var (
func registrationInfo() []types.LookupServiceRegistrationInfo {
vc := simulator.Map.Get(vim25.ServiceInstance).(*simulator.ServiceInstance)
setting := simulator.Map.OptionManager().Setting
sm := simulator.Map.SessionManager()
opts := make(map[string]string, len(setting))
for _, o := range setting {
@@ -42,14 +44,17 @@ func registrationInfo() []types.LookupServiceRegistrationInfo {
}
}
trust := []string{opts["vcsim.server.cert"]}
trust := []string{""}
if sm.TLSCert != nil {
trust[0] = sm.TLSCert()
}
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{
info := []types.LookupServiceRegistrationInfo{
{
LookupServiceRegistrationCommonServiceInfo: types.LookupServiceRegistrationCommonServiceInfo{
LookupServiceRegistrationMutableServiceInfo: types.LookupServiceRegistrationMutableServiceInfo{
@@ -68,7 +73,7 @@ func registrationInfo() []types.LookupServiceRegistrationInfo {
OwnerId: admin,
ServiceType: types.LookupServiceRegistrationServiceType{
Product: "com.vmware.cis",
Type: "sso:sts",
Type: "cs.identity",
},
},
ServiceId: siteID + ":" + uuid.New().String(),
@@ -126,4 +131,9 @@ func registrationInfo() []types.LookupServiceRegistrationInfo {
SiteId: siteID,
},
}
sts := info[0]
sts.ServiceType.Type = "sso:sts" // obsolete service type, but still used by PowerCLI
return append(info, sts)
}

View File

@@ -17,6 +17,8 @@ limitations under the License.
package simulator
import (
"sync"
"github.com/vmware/govmomi/lookup"
"github.com/vmware/govmomi/lookup/methods"
"github.com/vmware/govmomi/lookup/types"
@@ -32,6 +34,14 @@ var content = types.LookupServiceContent{
L10n: vim.ManagedObjectReference{Type: "LookupL10n", Value: "l10n"},
}
func init() {
simulator.RegisterEndpoint(func(s *simulator.Service, r *simulator.Registry) {
if r.IsVPX() {
s.RegisterSDK(New())
}
})
}
func New() *simulator.Registry {
r := simulator.NewRegistry()
r.Namespace = lookup.Namespace
@@ -40,10 +50,12 @@ func New() *simulator.Registry {
r.Put(&ServiceInstance{
ManagedObjectReference: lookup.ServiceInstance,
Content: content,
})
r.Put(&ServiceRegistration{
ManagedObjectReference: *content.ServiceRegistration,
Info: registrationInfo(),
register: func() {
r.Put(&ServiceRegistration{
ManagedObjectReference: *content.ServiceRegistration,
Info: registrationInfo(),
})
},
})
return r
@@ -53,9 +65,16 @@ type ServiceInstance struct {
vim.ManagedObjectReference
Content types.LookupServiceContent
instance sync.Once
register func()
}
func (s *ServiceInstance) RetrieveServiceContent(_ *types.RetrieveServiceContent) soap.HasFault {
// defer register to this point to ensure we can include vcsim's cert in ServiceEndpoints.SslTrust
// TODO: we should be able to register within New(), but this is the only place that currently depends on vcsim's cert.
s.instance.Do(s.register)
return &methods.RetrieveServiceContentBody{
Res: &types.RetrieveServiceContentResponse{
Returnval: s.Content,

View File

@@ -18,16 +18,15 @@ package nfc
import (
"context"
"errors"
"fmt"
"io"
"path"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/task"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/progress"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
@@ -195,7 +194,7 @@ func (l *Lease) Wait(ctx context.Context, items []types.OvfFileItem) (*LeaseInfo
}
if lease.Error != nil {
return nil, errors.New(lease.Error.LocalizedMessage)
return nil, &task.Error{LocalizedMethodFault: lease.Error}
}
return nil, fmt.Errorf("unexpected nfc lease state: %s", lease.State)
@@ -208,8 +207,6 @@ func (l *Lease) StartUpdater(ctx context.Context, info *LeaseInfo) *LeaseUpdater
func (l *Lease) Upload(ctx context.Context, item FileItem, f io.Reader, opts soap.Upload) error {
if opts.Progress == nil {
opts.Progress = item
} else {
opts.Progress = progress.Tee(item, opts.Progress)
}
// Non-disk files (such as .iso) use the PUT method.
@@ -230,8 +227,6 @@ func (l *Lease) Upload(ctx context.Context, item FileItem, f io.Reader, opts soa
func (l *Lease) DownloadFile(ctx context.Context, file string, item FileItem, opts soap.Download) error {
if opts.Progress == nil {
opts.Progress = item
} else {
opts.Progress = progress.Tee(item, opts.Progress)
}
return l.c.DownloadFile(ctx, file, item.URL, &opts)

View File

@@ -57,8 +57,8 @@ func (o FileItem) File() types.OvfFile {
}
type LeaseUpdater struct {
pos int64 // Number of bytes (keep first to ensure 64 bit aligment)
total int64 // Total number of bytes (keep first to ensure 64 bit aligment)
pos int64 // Number of bytes (keep first to ensure 64 bit alignment)
total int64 // Total number of bytes (keep first to ensure 64 bit alignment)
lease *Lease

View File

@@ -172,3 +172,50 @@ func (m AuthorizationManager) UpdateRole(ctx context.Context, id int32, name str
_, err := methods.UpdateAuthorizationRole(ctx, m.Client(), &req)
return err
}
func (m AuthorizationManager) HasUserPrivilegeOnEntities(ctx context.Context, entities []types.ManagedObjectReference, userName string, privID []string) ([]types.EntityPrivilege, error) {
req := types.HasUserPrivilegeOnEntities{
This: m.Reference(),
Entities: entities,
UserName: userName,
PrivId: privID,
}
res, err := methods.HasUserPrivilegeOnEntities(ctx, m.Client(), &req)
if err != nil {
return nil, err
}
return res.Returnval, nil
}
func (m AuthorizationManager) HasPrivilegeOnEntity(ctx context.Context, entity types.ManagedObjectReference, sessionID string, privID []string) ([]bool, error) {
req := types.HasPrivilegeOnEntity{
This: m.Reference(),
Entity: entity,
SessionId: sessionID,
PrivId: privID,
}
res, err := methods.HasPrivilegeOnEntity(ctx, m.Client(), &req)
if err != nil {
return nil, err
}
return res.Returnval, nil
}
func (m AuthorizationManager) FetchUserPrivilegeOnEntities(ctx context.Context, entities []types.ManagedObjectReference, userName string) ([]types.UserPrivilegeResult, error) {
req := types.FetchUserPrivilegeOnEntities{
This: m.Reference(),
Entities: entities,
UserName: userName,
}
res, err := methods.FetchUserPrivilegeOnEntities(ctx, m.Client(), &req)
if err != nil {
return nil, err
}
return res.Returnval, nil
}

View File

@@ -87,3 +87,17 @@ func (c ClusterComputeResource) MoveInto(ctx context.Context, hosts ...*HostSyst
return NewTask(c.c, res.Returnval), nil
}
func (c ClusterComputeResource) PlaceVm(ctx context.Context, spec types.PlacementSpec) (*types.PlacementResult, error) {
req := types.PlaceVm{
This: c.Reference(),
PlacementSpec: spec,
}
res, err := methods.PlaceVm(ctx, c.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}

View File

@@ -75,29 +75,22 @@ func (c *Common) SetInventoryPath(p string) {
c.InventoryPath = p
}
// ObjectName returns the base name of the InventoryPath field if set,
// otherwise fetches the mo.ManagedEntity.Name field via the property collector.
// ObjectName fetches the mo.ManagedEntity.Name field via the property collector.
func (c Common) ObjectName(ctx context.Context) (string, error) {
var o mo.ManagedEntity
var content []types.ObjectContent
err := c.Properties(ctx, c.Reference(), []string{"name"}, &o)
err := c.Properties(ctx, c.Reference(), []string{"name"}, &content)
if err != nil {
return "", err
}
if o.Name != "" {
return o.Name, nil
for i := range content {
for _, prop := range content[i].PropSet {
return prop.Val.(string), nil
}
}
// Network has its own "name" field...
var n mo.Network
err = c.Properties(ctx, c.Reference(), []string{"name"}, &n)
if err != nil {
return "", err
}
return n.Name, nil
return "", nil
}
// Properties is a wrapper for property.DefaultCollector().RetrieveOne()
@@ -142,3 +135,14 @@ func (c Common) SetCustomValue(ctx context.Context, key string, value string) er
_, err := methods.SetCustomValue(ctx, c.c, &req)
return err
}
func ReferenceFromString(s string) *types.ManagedObjectReference {
var ref types.ManagedObjectReference
if !ref.FromString(s) {
return nil
}
if mo.IsManagedObjectType(ref.Type) {
return &ref
}
return nil
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
@@ -36,6 +37,12 @@ func NewCustomizationSpecManager(c *vim25.Client) *CustomizationSpecManager {
return &cs
}
func (cs CustomizationSpecManager) Info(ctx context.Context) ([]types.CustomizationSpecInfo, error) {
var m mo.CustomizationSpecManager
err := cs.Properties(ctx, cs.Reference(), []string{"info"}, &m)
return m.Info, err
}
func (cs CustomizationSpecManager) DoesCustomizationSpecExist(ctx context.Context, name string) (bool, error) {
req := types.DoesCustomizationSpecExist{
This: cs.Reference(),

View File

@@ -68,6 +68,11 @@ func NewDatastore(c *vim25.Client, ref types.ManagedObjectReference) *Datastore
}
func (d Datastore) Path(path string) string {
var p DatastorePath
if p.FromString(path) {
return p.String() // already in "[datastore] path" format
}
return (&DatastorePath{
Datastore: d.Name(),
Path: path,

View File

@@ -297,10 +297,8 @@ func (f *DatastoreFile) TailFunc(lines int, include func(line int, message strin
nread = bsize + remain
eof = true
} else {
if pos, err = f.Seek(offset, io.SeekEnd); err != nil {
return err
}
} else if pos, err = f.Seek(offset, io.SeekEnd); err != nil {
return err
}
if _, err = io.CopyN(buf, f, nread); err != nil {

View File

@@ -31,7 +31,7 @@ type DatastorePath struct {
// FromString parses a datastore path.
// Returns true if the path could be parsed, false otherwise.
func (p *DatastorePath) FromString(s string) bool {
if len(s) == 0 {
if s == "" {
return false
}

View File

@@ -71,10 +71,8 @@ func (m DiagnosticManager) GenerateLogBundles(ctx context.Context, includeDefaul
IncludeDefault: includeDefault,
}
if host != nil {
for _, h := range host {
req.Host = append(req.Host, h.Reference())
}
for _, h := range host {
req.Host = append(req.Host, h.Reference())
}
res, err := methods.GenerateLogBundles_Task(ctx, m.c, &req)

View File

@@ -36,6 +36,10 @@ func NewDistributedVirtualPortgroup(c *vim25.Client, ref types.ManagedObjectRefe
}
}
func (p DistributedVirtualPortgroup) GetInventoryPath() string {
return p.InventoryPath
}
// EthernetCardBackingInfo returns the VirtualDeviceBackingInfo for this DistributedVirtualPortgroup
func (p DistributedVirtualPortgroup) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
var dvp mo.DistributedVirtualPortgroup
@@ -46,9 +50,15 @@ func (p DistributedVirtualPortgroup) EthernetCardBackingInfo(ctx context.Context
return nil, err
}
// From the docs at https://code.vmware.com/apis/196/vsphere/doc/vim.dvs.DistributedVirtualPortgroup.ConfigInfo.html:
// "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."
// Note that "the object" refers to the Switch, not the PortGroup.
if dvp.Config.DistributedVirtualSwitch == nil {
return nil, fmt.Errorf("no System.Read privilege on: %s.%s", p.Reference(), prop)
name := p.InventoryPath
if name == "" {
name = p.Reference().String()
}
return nil, fmt.Errorf("failed to create EthernetCardBackingInfo for %s: System.Read privilege required for %s", name, prop)
}
if err := p.Properties(ctx, *dvp.Config.DistributedVirtualSwitch, []string{"uuid"}, &dvs); err != nil {

View File

@@ -18,6 +18,7 @@ package object
import (
"context"
"fmt"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
@@ -34,8 +35,17 @@ func NewDistributedVirtualSwitch(c *vim25.Client, ref types.ManagedObjectReferen
}
}
func (s DistributedVirtualSwitch) GetInventoryPath() string {
return s.InventoryPath
}
func (s DistributedVirtualSwitch) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
return nil, ErrNotSupported // TODO: just to satisfy NetworkReference interface for the finder
ref := s.Reference()
name := s.InventoryPath
if name == "" {
name = ref.String()
}
return nil, fmt.Errorf("type %s (%s) cannot be used for EthernetCardBackingInfo", ref.Type, name)
}
func (s DistributedVirtualSwitch) Reconfigure(ctx context.Context, spec types.BaseDVSConfigSpec) (*Task, error) {
@@ -78,3 +88,31 @@ func (s DistributedVirtualSwitch) FetchDVPorts(ctx context.Context, criteria *ty
}
return res.Returnval, nil
}
func (s DistributedVirtualSwitch) ReconfigureDVPort(ctx context.Context, spec []types.DVPortConfigSpec) (*Task, error) {
req := types.ReconfigureDVPort_Task{
This: s.Reference(),
Port: spec,
}
res, err := methods.ReconfigureDVPort_Task(ctx, s.Client(), &req)
if err != nil {
return nil, err
}
return NewTask(s.Client(), res.Returnval), nil
}
func (s DistributedVirtualSwitch) ReconfigureLACP(ctx context.Context, spec []types.VMwareDvsLacpGroupSpec) (*Task, error) {
req := types.UpdateDVSLacpGroupConfig_Task{
This: s.Reference(),
LacpGroupSpec: spec,
}
res, err := methods.UpdateDVSLacpGroupConfig_Task(ctx, s.Client(), &req)
if err != nil {
return nil, err
}
return NewTask(s.Client(), res.Returnval), nil
}

View File

@@ -225,3 +225,17 @@ func (f Folder) MoveInto(ctx context.Context, list []types.ManagedObjectReferenc
return NewTask(f.c, res.Returnval), nil
}
func (f Folder) PlaceVmsXCluster(ctx context.Context, spec types.PlaceVmsXClusterSpec) (*types.PlaceVmsXClusterResult, error) {
req := types.PlaceVmsXCluster{
This: f.Reference(),
PlacementSpec: spec,
}
res, err := methods.PlaceVmsXCluster(ctx, f.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}

View File

@@ -1,72 +0,0 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package object
import (
"context"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
)
type HistoryCollector struct {
Common
}
func NewHistoryCollector(c *vim25.Client, ref types.ManagedObjectReference) *HistoryCollector {
return &HistoryCollector{
Common: NewCommon(c, ref),
}
}
func (h HistoryCollector) Destroy(ctx context.Context) error {
req := types.DestroyCollector{
This: h.Reference(),
}
_, err := methods.DestroyCollector(ctx, h.c, &req)
return err
}
func (h HistoryCollector) Reset(ctx context.Context) error {
req := types.ResetCollector{
This: h.Reference(),
}
_, err := methods.ResetCollector(ctx, h.c, &req)
return err
}
func (h HistoryCollector) Rewind(ctx context.Context) error {
req := types.RewindCollector{
This: h.Reference(),
}
_, err := methods.RewindCollector(ctx, h.c, &req)
return err
}
func (h HistoryCollector) SetPageSize(ctx context.Context, maxCount int32) error {
req := types.SetCollectorPageSize{
This: h.Reference(),
MaxCount: maxCount,
}
_, err := methods.SetCollectorPageSize(ctx, h.c, &req)
return err
}

View File

@@ -18,9 +18,9 @@ package object
import (
"context"
"fmt"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
@@ -34,163 +34,138 @@ func NewHostConfigManager(c *vim25.Client, ref types.ManagedObjectReference) *Ho
}
}
func (m HostConfigManager) DatastoreSystem(ctx context.Context) (*HostDatastoreSystem, error) {
var h mo.HostSystem
// reference returns the ManagedObjectReference for the given HostConfigManager property name.
// An error is returned if the field is nil, of type ErrNotSupported if versioned is true.
func (m HostConfigManager) reference(ctx context.Context, name string, versioned ...bool) (types.ManagedObjectReference, error) {
prop := "configManager." + name
var content []types.ObjectContent
err := m.Properties(ctx, m.Reference(), []string{"configManager.datastoreSystem"}, &h)
err := m.Properties(ctx, m.Reference(), []string{prop}, &content)
if err != nil {
return types.ManagedObjectReference{}, err
}
for _, c := range content {
for _, p := range c.PropSet {
if p.Name != prop {
continue
}
if ref, ok := p.Val.(types.ManagedObjectReference); ok {
return ref, nil
}
}
}
err = fmt.Errorf("%s %s is nil", m.Reference(), prop)
if len(versioned) == 1 && versioned[0] {
err = ErrNotSupported
}
return types.ManagedObjectReference{}, err
}
func (m HostConfigManager) DatastoreSystem(ctx context.Context) (*HostDatastoreSystem, error) {
ref, err := m.reference(ctx, "datastoreSystem")
if err != nil {
return nil, err
}
return NewHostDatastoreSystem(m.c, *h.ConfigManager.DatastoreSystem), nil
return NewHostDatastoreSystem(m.c, ref), nil
}
func (m HostConfigManager) NetworkSystem(ctx context.Context) (*HostNetworkSystem, error) {
var h mo.HostSystem
err := m.Properties(ctx, m.Reference(), []string{"configManager.networkSystem"}, &h)
ref, err := m.reference(ctx, "networkSystem")
if err != nil {
return nil, err
}
return NewHostNetworkSystem(m.c, *h.ConfigManager.NetworkSystem), nil
return NewHostNetworkSystem(m.c, ref), nil
}
func (m HostConfigManager) FirewallSystem(ctx context.Context) (*HostFirewallSystem, error) {
var h mo.HostSystem
err := m.Properties(ctx, m.Reference(), []string{"configManager.firewallSystem"}, &h)
ref, err := m.reference(ctx, "firewallSystem")
if err != nil {
return nil, err
}
return NewHostFirewallSystem(m.c, *h.ConfigManager.FirewallSystem), nil
return NewHostFirewallSystem(m.c, ref), nil
}
func (m HostConfigManager) StorageSystem(ctx context.Context) (*HostStorageSystem, error) {
var h mo.HostSystem
err := m.Properties(ctx, m.Reference(), []string{"configManager.storageSystem"}, &h)
ref, err := m.reference(ctx, "storageSystem")
if err != nil {
return nil, err
}
return NewHostStorageSystem(m.c, *h.ConfigManager.StorageSystem), nil
return NewHostStorageSystem(m.c, ref), nil
}
func (m HostConfigManager) VirtualNicManager(ctx context.Context) (*HostVirtualNicManager, error) {
var h mo.HostSystem
err := m.Properties(ctx, m.Reference(), []string{"configManager.virtualNicManager"}, &h)
ref, err := m.reference(ctx, "virtualNicManager")
if err != nil {
return nil, err
}
return NewHostVirtualNicManager(m.c, *h.ConfigManager.VirtualNicManager, m.Reference()), nil
return NewHostVirtualNicManager(m.c, ref, m.Reference()), nil
}
func (m HostConfigManager) VsanSystem(ctx context.Context) (*HostVsanSystem, error) {
var h mo.HostSystem
err := m.Properties(ctx, m.Reference(), []string{"configManager.vsanSystem"}, &h)
ref, err := m.reference(ctx, "vsanSystem", true) // Added in 5.5
if err != nil {
return nil, err
}
// Added in 5.5
if h.ConfigManager.VsanSystem == nil {
return nil, ErrNotSupported
}
return NewHostVsanSystem(m.c, *h.ConfigManager.VsanSystem), nil
return NewHostVsanSystem(m.c, ref), nil
}
func (m HostConfigManager) VsanInternalSystem(ctx context.Context) (*HostVsanInternalSystem, error) {
var h mo.HostSystem
err := m.Properties(ctx, m.Reference(), []string{"configManager.vsanInternalSystem"}, &h)
ref, err := m.reference(ctx, "vsanInternalSystem", true) // Added in 5.5
if err != nil {
return nil, err
}
// Added in 5.5
if h.ConfigManager.VsanInternalSystem == nil {
return nil, ErrNotSupported
}
return NewHostVsanInternalSystem(m.c, *h.ConfigManager.VsanInternalSystem), nil
return NewHostVsanInternalSystem(m.c, ref), nil
}
func (m HostConfigManager) AccountManager(ctx context.Context) (*HostAccountManager, error) {
var h mo.HostSystem
err := m.Properties(ctx, m.Reference(), []string{"configManager.accountManager"}, &h)
ref, err := m.reference(ctx, "accountManager", true) // Added in 5.5
if err != nil {
return nil, err
}
ref := h.ConfigManager.AccountManager // Added in 6.0
if ref == nil {
// Versions < 5.5 can use the ServiceContent ref,
// but we can only use it when connected directly to ESX.
c := m.Client()
if !c.IsVC() {
ref = c.ServiceContent.AccountManager
}
if ref == nil {
return nil, ErrNotSupported
if err == ErrNotSupported {
// Versions < 5.5 can use the ServiceContent ref,
// but only when connected directly to ESX.
if m.c.ServiceContent.AccountManager == nil {
return nil, err
}
ref = *m.c.ServiceContent.AccountManager
} else {
return nil, err
}
}
return NewHostAccountManager(m.c, *ref), nil
return NewHostAccountManager(m.c, ref), nil
}
func (m HostConfigManager) OptionManager(ctx context.Context) (*OptionManager, error) {
var h mo.HostSystem
err := m.Properties(ctx, m.Reference(), []string{"configManager.advancedOption"}, &h)
ref, err := m.reference(ctx, "advancedOption")
if err != nil {
return nil, err
}
return NewOptionManager(m.c, *h.ConfigManager.AdvancedOption), nil
return NewOptionManager(m.c, ref), nil
}
func (m HostConfigManager) ServiceSystem(ctx context.Context) (*HostServiceSystem, error) {
var h mo.HostSystem
err := m.Properties(ctx, m.Reference(), []string{"configManager.serviceSystem"}, &h)
ref, err := m.reference(ctx, "serviceSystem")
if err != nil {
return nil, err
}
return NewHostServiceSystem(m.c, *h.ConfigManager.ServiceSystem), nil
return NewHostServiceSystem(m.c, ref), nil
}
func (m HostConfigManager) CertificateManager(ctx context.Context) (*HostCertificateManager, error) {
var h mo.HostSystem
err := m.Properties(ctx, m.Reference(), []string{"configManager.certificateManager"}, &h)
ref, err := m.reference(ctx, "certificateManager", true) // Added in 6.0
if err != nil {
return nil, err
}
// Added in 6.0
if h.ConfigManager.CertificateManager == nil {
return nil, ErrNotSupported
}
return NewHostCertificateManager(m.c, *h.ConfigManager.CertificateManager, m.Reference()), nil
return NewHostCertificateManager(m.c, ref, m.Reference()), nil
}
func (m HostConfigManager) DateTimeSystem(ctx context.Context) (*HostDateTimeSystem, error) {
var h mo.HostSystem
err := m.Properties(ctx, m.Reference(), []string{"configManager.dateTimeSystem"}, &h)
ref, err := m.reference(ctx, "dateTimeSystem")
if err != nil {
return nil, err
}
return NewHostDateTimeSystem(m.c, *h.ConfigManager.DateTimeSystem), nil
return NewHostDateTimeSystem(m.c, ref), nil
}

View File

@@ -117,3 +117,19 @@ func (s HostDatastoreSystem) QueryVmfsDatastoreCreateOptions(ctx context.Context
return res.Returnval, nil
}
func (s HostDatastoreSystem) ResignatureUnresolvedVmfsVolumes(ctx context.Context, devicePaths []string) (*Task, error) {
req := &types.ResignatureUnresolvedVmfsVolume_Task{
This: s.Reference(),
ResolutionSpec: types.HostUnresolvedVmfsResignatureSpec{
ExtentDevicePath: devicePaths,
},
}
res, err := methods.ResignatureUnresolvedVmfsVolume_Task(ctx, s.Client(), req)
if err != nil {
return nil, err
}
return NewTask(s.c, res.Returnval), nil
}

View File

@@ -98,18 +98,18 @@ func (o HostNetworkSystem) AddVirtualSwitch(ctx context.Context, vswitchName str
}
// QueryNetworkHint wraps methods.QueryNetworkHint
func (o HostNetworkSystem) QueryNetworkHint(ctx context.Context, device []string) error {
func (o HostNetworkSystem) QueryNetworkHint(ctx context.Context, device []string) ([]types.PhysicalNicHintInfo, error) {
req := types.QueryNetworkHint{
This: o.Reference(),
Device: device,
}
_, err := methods.QueryNetworkHint(ctx, o.c, &req)
res, err := methods.QueryNetworkHint(ctx, o.c, &req)
if err != nil {
return err
return nil, err
}
return nil
return res.Returnval, err
}
// RefreshNetworkSystem wraps methods.RefreshNetworkSystem

View File

@@ -172,3 +172,29 @@ func (s HostStorageSystem) AttachScsiLun(ctx context.Context, uuid string) error
return err
}
func (s HostStorageSystem) QueryUnresolvedVmfsVolumes(ctx context.Context) ([]types.HostUnresolvedVmfsVolume, error) {
req := &types.QueryUnresolvedVmfsVolume{
This: s.Reference(),
}
res, err := methods.QueryUnresolvedVmfsVolume(ctx, s.Client(), req)
if err != nil {
return nil, err
}
return res.Returnval, nil
}
func (s HostStorageSystem) UnmountVmfsVolume(ctx context.Context, vmfsUuid string) error {
req := &types.UnmountVmfsVolume{
This: s.Reference(),
VmfsUuid: vmfsUuid,
}
_, err := methods.UnmountVmfsVolume(ctx, s.Client(), req)
if err != nil {
return err
}
return nil
}

View File

@@ -21,6 +21,7 @@ import (
"fmt"
"net"
"github.com/vmware/govmomi/internal"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
@@ -81,17 +82,17 @@ func (h HostSystem) ManagementIPs(ctx context.Context) ([]net.IP, error) {
return nil, err
}
var ips []net.IP
for _, nc := range mh.Config.VirtualNicManagerInfo.NetConfig {
if nc.NicType == "management" && len(nc.CandidateVnic) > 0 {
ip := net.ParseIP(nc.CandidateVnic[0].Spec.Ip.IpAddress)
if ip != nil {
ips = append(ips, ip)
}
}
config := mh.Config
if config == nil {
return nil, nil
}
return ips, nil
info := config.VirtualNicManagerInfo
if info == nil {
return nil, nil
}
return internal.HostSystemManagementIPs(info.NetConfig), nil
}
func (h HostSystem) Disconnect(ctx context.Context) (*Task, error) {

View File

@@ -20,7 +20,6 @@ import (
"context"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
@@ -34,21 +33,20 @@ func NewNetwork(c *vim25.Client, ref types.ManagedObjectReference) *Network {
}
}
func (n Network) GetInventoryPath() string {
return n.InventoryPath
}
// EthernetCardBackingInfo returns the VirtualDeviceBackingInfo for this Network
func (n Network) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
var e mo.Network
// Use Network.Name rather than Common.Name as the latter does not return the complete name if it contains a '/'
// We can't use Common.ObjectName here either as we need the ManagedEntity.Name field is not set since mo.Network
// has its own Name field.
err := n.Properties(ctx, n.Reference(), []string{"name"}, &e)
name, err := n.ObjectName(ctx)
if err != nil {
return nil, err
}
backing := &types.VirtualEthernetCardNetworkBackingInfo{
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
DeviceName: e.Name,
DeviceName: name,
},
}

View File

@@ -26,6 +26,6 @@ import (
// which can be used as the backing for a VirtualEthernetCard.
type NetworkReference interface {
Reference
GetInventoryPath() string
EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error)
}

View File

@@ -35,19 +35,17 @@ func NewOpaqueNetwork(c *vim25.Client, ref types.ManagedObjectReference) *Opaque
}
}
func (n OpaqueNetwork) GetInventoryPath() string {
return n.InventoryPath
}
// EthernetCardBackingInfo returns the VirtualDeviceBackingInfo for this Network
func (n OpaqueNetwork) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
var net mo.OpaqueNetwork
if err := n.Properties(ctx, n.Reference(), []string{"summary"}, &net); err != nil {
summary, err := n.Summary(ctx)
if err != nil {
return nil, err
}
summary, ok := net.Summary.(*types.OpaqueNetworkSummary)
if !ok {
return nil, fmt.Errorf("%s unsupported network type: %T", n, net.Summary)
}
backing := &types.VirtualEthernetCardOpaqueNetworkBackingInfo{
OpaqueNetworkId: summary.OpaqueNetworkId,
OpaqueNetworkType: summary.OpaqueNetworkType,
@@ -55,3 +53,20 @@ func (n OpaqueNetwork) EthernetCardBackingInfo(ctx context.Context) (types.BaseV
return backing, nil
}
// Summary returns the mo.OpaqueNetwork.Summary property
func (n OpaqueNetwork) Summary(ctx context.Context) (*types.OpaqueNetworkSummary, error) {
var props mo.OpaqueNetwork
err := n.Properties(ctx, n.Reference(), []string{"summary"}, &props)
if err != nil {
return nil, err
}
summary, ok := props.Summary.(*types.OpaqueNetworkSummary)
if !ok {
return nil, fmt.Errorf("%s unsupported network summary type: %T", n, props.Summary)
}
return summary, nil
}

View File

@@ -22,6 +22,7 @@ import (
"github.com/vmware/govmomi/nfc"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
@@ -35,6 +36,18 @@ func NewResourcePool(c *vim25.Client, ref types.ManagedObjectReference) *Resourc
}
}
// Owner returns the ResourcePool owner as a ClusterComputeResource or ComputeResource.
func (p ResourcePool) Owner(ctx context.Context) (Reference, error) {
var pool mo.ResourcePool
err := p.Properties(ctx, p.Reference(), []string{"owner"}, &pool)
if err != nil {
return nil, err
}
return NewReference(p.Client(), pool.Owner), nil
}
func (p ResourcePool) ImportVApp(ctx context.Context, spec types.BaseImportSpec, folder *Folder, host *HostSystem) (*nfc.Lease, error) {
req := types.ImportVApp{
This: p.Reference(),

View File

@@ -161,3 +161,88 @@ func (s SearchIndex) FindChild(ctx context.Context, entity Reference, name strin
}
return NewReference(s.c, *res.Returnval), nil
}
// FindAllByDnsName finds all virtual machines or hosts by DNS name.
func (s SearchIndex) FindAllByDnsName(ctx context.Context, dc *Datacenter, dnsName string, vmSearch bool) ([]Reference, error) {
req := types.FindAllByDnsName{
This: s.Reference(),
DnsName: dnsName,
VmSearch: vmSearch,
}
if dc != nil {
ref := dc.Reference()
req.Datacenter = &ref
}
res, err := methods.FindAllByDnsName(ctx, s.c, &req)
if err != nil {
return nil, err
}
if len(res.Returnval) == 0 {
return nil, nil
}
var references []Reference
for _, returnval := range res.Returnval {
references = append(references, NewReference(s.c, returnval))
}
return references, nil
}
// FindAllByIp finds all virtual machines or hosts by IP address.
func (s SearchIndex) FindAllByIp(ctx context.Context, dc *Datacenter, ip string, vmSearch bool) ([]Reference, error) {
req := types.FindAllByIp{
This: s.Reference(),
Ip: ip,
VmSearch: vmSearch,
}
if dc != nil {
ref := dc.Reference()
req.Datacenter = &ref
}
res, err := methods.FindAllByIp(ctx, s.c, &req)
if err != nil {
return nil, err
}
if len(res.Returnval) == 0 {
return nil, nil
}
var references []Reference
for _, returnval := range res.Returnval {
references = append(references, NewReference(s.c, returnval))
}
return references, nil
}
// FindAllByUuid finds all virtual machines or hosts by UUID.
func (s SearchIndex) FindAllByUuid(ctx context.Context, dc *Datacenter, uuid string, vmSearch bool, instanceUuid *bool) ([]Reference, error) {
req := types.FindAllByUuid{
This: s.Reference(),
Uuid: uuid,
VmSearch: vmSearch,
InstanceUuid: instanceUuid,
}
if dc != nil {
ref := dc.Reference()
req.Datacenter = &ref
}
res, err := methods.FindAllByUuid(ctx, s.c, &req)
if err != nil {
return nil, err
}
if len(res.Returnval) == 0 {
return nil, nil
}
var references []Reference
for _, returnval := range res.Returnval {
references = append(references, NewReference(s.c, returnval))
}
return references, nil
}

View File

@@ -48,9 +48,13 @@ func (t *Task) Wait(ctx context.Context) error {
return err
}
func (t *Task) WaitForResult(ctx context.Context, s progress.Sinker) (*types.TaskInfo, error) {
func (t *Task) WaitForResult(ctx context.Context, s ...progress.Sinker) (*types.TaskInfo, error) {
var pr progress.Sinker
if len(s) == 1 {
pr = s[0]
}
p := property.DefaultCollector(t.c)
return task.Wait(ctx, t.Reference(), p, s)
return task.Wait(ctx, t.Reference(), p, pr)
}
func (t *Task) Cancel(ctx context.Context) error {
@@ -60,3 +64,37 @@ func (t *Task) Cancel(ctx context.Context) error {
return err
}
// SetState sets task state and optionally sets results or fault, as appropriate for state.
func (t *Task) SetState(ctx context.Context, state types.TaskInfoState, result types.AnyType, fault *types.LocalizedMethodFault) error {
req := types.SetTaskState{
This: t.Reference(),
State: state,
Result: result,
Fault: fault,
}
_, err := methods.SetTaskState(ctx, t.Common.Client(), &req)
return err
}
// SetDescription updates task description to describe the current phase of the task.
func (t *Task) SetDescription(ctx context.Context, description types.LocalizableMessage) error {
req := types.SetTaskDescription{
This: t.Reference(),
Description: description,
}
_, err := methods.SetTaskDescription(ctx, t.Common.Client(), &req)
return err
}
// UpdateProgress Sets percentage done for this task and recalculates overall percentage done.
// If a percentDone value of less than zero or greater than 100 is specified,
// a value of zero or 100 respectively is used.
func (t *Task) UpdateProgress(ctx context.Context, percentDone int) error {
req := types.UpdateProgress{
This: t.Reference(),
PercentDone: int32(percentDone),
}
_, err := methods.UpdateProgress(ctx, t.Common.Client(), &req)
return err
}

View File

@@ -0,0 +1,78 @@
/*
Copyright (c) 2021 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package object
import (
"context"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
)
type TenantManager struct {
Common
}
func NewTenantManager(c *vim25.Client) *TenantManager {
t := TenantManager{
Common: NewCommon(c, *c.ServiceContent.TenantManager),
}
return &t
}
func (t TenantManager) MarkServiceProviderEntities(ctx context.Context, entities []types.ManagedObjectReference) error {
req := types.MarkServiceProviderEntities{
This: t.Reference(),
Entity: entities,
}
_, err := methods.MarkServiceProviderEntities(ctx, t.Client(), &req)
if err != nil {
return err
}
return nil
}
func (t TenantManager) UnmarkServiceProviderEntities(ctx context.Context, entities []types.ManagedObjectReference) error {
req := types.UnmarkServiceProviderEntities{
This: t.Reference(),
Entity: entities,
}
_, err := methods.UnmarkServiceProviderEntities(ctx, t.Client(), &req)
if err != nil {
return err
}
return nil
}
func (t TenantManager) RetrieveServiceProviderEntities(ctx context.Context) ([]types.ManagedObjectReference, error) {
req := types.RetrieveServiceProviderEntities{
This: t.Reference(),
}
res, err := methods.RetrieveServiceProviderEntities(ctx, t.Client(), &req)
if err != nil {
return nil, err
}
return res.Returnval, nil
}

View File

@@ -103,3 +103,19 @@ func (p VirtualApp) Suspend(ctx context.Context) (*Task, error) {
return NewTask(p.c, res.Returnval), nil
}
func (p VirtualApp) Clone(ctx context.Context, name string, target types.ManagedObjectReference, spec types.VAppCloneSpec) (*Task, error) {
req := types.CloneVApp_Task{
This: p.Reference(),
Name: name,
Target: target,
Spec: spec,
}
res, err := methods.CloneVApp_Task(ctx, p.c, &req)
if err != nil {
return nil, err
}
return NewTask(p.c, res.Returnval), nil
}

View File

@@ -19,6 +19,7 @@ package object
import (
"errors"
"fmt"
"math/rand"
"path/filepath"
"reflect"
"regexp"
@@ -63,11 +64,12 @@ func EthernetCardTypes() VirtualDeviceList {
&types.VirtualE1000e{},
&types.VirtualVmxnet2{},
&types.VirtualVmxnet3{},
&types.VirtualVmxnet3Vrdma{},
&types.VirtualPCNet32{},
&types.VirtualSriovEthernetCard{},
}).Select(func(device types.BaseVirtualDevice) bool {
c := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
c.GetVirtualDevice().Key = -1
c.GetVirtualDevice().Key = VirtualDeviceList{}.newRandomKey()
return true
})
}
@@ -134,6 +136,9 @@ func (l VirtualDeviceList) SelectByBackingInfo(backing types.BaseVirtualDeviceBa
b := backing.(*types.VirtualEthernetCardDistributedVirtualPortBackingInfo)
return a.Port.SwitchUuid == b.Port.SwitchUuid &&
a.Port.PortgroupKey == b.Port.PortgroupKey
case *types.VirtualEthernetCardOpaqueNetworkBackingInfo:
b := backing.(*types.VirtualEthernetCardOpaqueNetworkBackingInfo)
return a.OpaqueNetworkId == b.OpaqueNetworkId
case *types.VirtualDiskFlatVer2BackingInfo:
b := backing.(*types.VirtualDiskFlatVer2BackingInfo)
if a.Parent != nil && b.Parent != nil {
@@ -146,6 +151,25 @@ func (l VirtualDeviceList) SelectByBackingInfo(backing types.BaseVirtualDeviceBa
case types.BaseVirtualDeviceFileBackingInfo:
b := backing.(types.BaseVirtualDeviceFileBackingInfo)
return a.GetVirtualDeviceFileBackingInfo().FileName == b.GetVirtualDeviceFileBackingInfo().FileName
case *types.VirtualPCIPassthroughVmiopBackingInfo:
b := backing.(*types.VirtualPCIPassthroughVmiopBackingInfo)
return a.Vgpu == b.Vgpu
case *types.VirtualPCIPassthroughDynamicBackingInfo:
b := backing.(*types.VirtualPCIPassthroughDynamicBackingInfo)
if b.CustomLabel != "" && b.CustomLabel != a.CustomLabel {
return false
}
if len(b.AllowedDevice) == 0 {
return true
}
for _, x := range a.AllowedDevice {
for _, y := range b.AllowedDevice {
if x.DeviceId == y.DeviceId && x.VendorId == y.VendorId {
return true
}
}
}
return false
default:
return false
}
@@ -229,8 +253,10 @@ func (l VirtualDeviceList) FindSCSIController(name string) (*types.VirtualSCSICo
func (l VirtualDeviceList) CreateSCSIController(name string) (types.BaseVirtualDevice, error) {
ctypes := SCSIControllerTypes()
if name == "scsi" || name == "" {
if name == "" || name == "scsi" {
name = ctypes.Type(ctypes[0])
} else if name == "virtualscsi" {
name = "pvscsi" // ovf VirtualSCSI mapping
}
found := ctypes.Select(func(device types.BaseVirtualDevice) bool {
@@ -431,10 +457,22 @@ func (l VirtualDeviceList) AssignController(device types.BaseVirtualDevice, c ty
d.UnitNumber = new(int32)
*d.UnitNumber = l.newUnitNumber(c)
if d.Key == 0 {
d.Key = -1
d.Key = l.newRandomKey()
}
}
// newRandomKey returns a random negative device key.
// The generated key can be used for devices you want to add so that it does not collide with existing ones.
func (l VirtualDeviceList) newRandomKey() int32 {
// NOTE: rand.Uint32 cannot be used here because conversion from uint32 to int32 may change the sign
key := rand.Int31() * -1
if key == 0 {
return -1
}
return key
}
// CreateDisk creates a new VirtualDisk device which can be added to a VM.
func (l VirtualDeviceList) CreateDisk(c types.BaseVirtualController, ds types.ManagedObjectReference, name string) *types.VirtualDisk {
// If name is not specified, one will be chosen for you.
@@ -862,6 +900,8 @@ func (l VirtualDeviceList) Type(device types.BaseVirtualDevice) string {
return "lsilogic-sas"
case *types.VirtualNVMEController:
return "nvme"
case *types.VirtualPrecisionClock:
return "clock"
default:
return l.deviceName(device)
}
@@ -879,7 +919,13 @@ func (l VirtualDeviceList) Name(device types.BaseVirtualDevice) string {
dtype := l.Type(device)
switch dtype {
case DeviceTypeEthernet:
key = fmt.Sprintf("%d", UnitNumber-7)
// Ethernet devices of UnitNumber 7-19 are non-SRIOV. Ethernet devices of
// UnitNumber 45-36 descending are SRIOV
if UnitNumber <= 45 && UnitNumber >= 36 {
key = fmt.Sprintf("sriov-%d", 45-UnitNumber)
} else {
key = fmt.Sprintf("%d", UnitNumber-7)
}
case DeviceTypeDisk:
key = fmt.Sprintf("%d-%d", d.ControllerKey, UnitNumber)
default:
@@ -907,25 +953,9 @@ func (l VirtualDeviceList) ConfigSpec(op types.VirtualDeviceConfigSpecOperation)
var res []types.BaseVirtualDeviceConfigSpec
for _, device := range l {
config := &types.VirtualDeviceConfigSpec{
Device: device,
Operation: op,
}
if disk, ok := device.(*types.VirtualDisk); ok {
config.FileOperation = fop
// Special case to attach an existing disk
if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 {
childDisk := false
if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
childDisk = b.Parent != nil
}
if !childDisk {
// Existing disk, clear file operation
config.FileOperation = ""
}
}
Device: device,
Operation: op,
FileOperation: diskFileOperation(op, fop, device),
}
res = append(res, config)

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2015-2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2015-2021 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import (
"fmt"
"net"
"path"
"strings"
"github.com/vmware/govmomi/nfc"
"github.com/vmware/govmomi/property"
@@ -33,12 +34,41 @@ import (
const (
PropRuntimePowerState = "summary.runtime.powerState"
PropConfigTemplate = "summary.config.template"
)
type VirtualMachine struct {
Common
}
// extractDiskLayoutFiles is a helper function used to extract file keys for
// all disk files attached to the virtual machine at the current point of
// running.
func extractDiskLayoutFiles(diskLayoutList []types.VirtualMachineFileLayoutExDiskLayout) []int {
var result []int
for _, layoutExDisk := range diskLayoutList {
for _, link := range layoutExDisk.Chain {
for i := range link.FileKey { // diskDescriptor, diskExtent pairs
result = append(result, int(link.FileKey[i]))
}
}
}
return result
}
// removeKey is a helper function for removing a specific file key from a list
// of keys associated with disks attached to a virtual machine.
func removeKey(l *[]int, key int) {
for i, k := range *l {
if k == key {
*l = append((*l)[:i], (*l)[i+1:]...)
break
}
}
}
func NewVirtualMachine(c *vim25.Client, ref types.ManagedObjectReference) *VirtualMachine {
return &VirtualMachine{
Common: NewCommon(c, ref),
@@ -56,6 +86,17 @@ func (v VirtualMachine) PowerState(ctx context.Context) (types.VirtualMachinePow
return o.Summary.Runtime.PowerState, nil
}
func (v VirtualMachine) IsTemplate(ctx context.Context) (bool, error) {
var o mo.VirtualMachine
err := v.Properties(ctx, v.Reference(), []string{PropConfigTemplate}, &o)
if err != nil {
return false, err
}
return o.Summary.Config.Template, nil
}
func (v VirtualMachine) PowerOn(ctx context.Context) (*Task, error) {
req := types.PowerOnVM_Task{
This: v.Reference(),
@@ -169,6 +210,20 @@ func (v VirtualMachine) Clone(ctx context.Context, folder *Folder, name string,
return NewTask(v.c, res.Returnval), nil
}
func (v VirtualMachine) InstantClone(ctx context.Context, config types.VirtualMachineInstantCloneSpec) (*Task, error) {
req := types.InstantClone_Task{
This: v.Reference(),
Spec: config,
}
res, err := methods.InstantClone_Task(ctx, v.c, &req)
if err != nil {
return nil, err
}
return NewTask(v.c, res.Returnval), nil
}
func (v VirtualMachine) Customize(ctx context.Context, spec types.CustomizationSpec) (*Task, error) {
req := types.CustomizeVM_Task{
This: v.Reference(),
@@ -221,7 +276,9 @@ func (v VirtualMachine) RefreshStorageInfo(ctx context.Context) error {
return err
}
func (v VirtualMachine) WaitForIP(ctx context.Context) (string, error) {
// WaitForIP waits for the VM guest.ipAddress property to report an IP address.
// Waits for an IPv4 address if the v4 param is true.
func (v VirtualMachine) WaitForIP(ctx context.Context, v4 ...bool) (string, error) {
var ip string
p := property.DefaultCollector(v.c)
@@ -238,6 +295,11 @@ func (v VirtualMachine) WaitForIP(ctx context.Context) (string, error) {
}
ip = c.Val.(string)
if len(v4) == 1 && v4[0] {
if net.ParseIP(ip).To4() == nil {
return false
}
}
return true
}
@@ -272,7 +334,9 @@ func (v VirtualMachine) WaitForNetIP(ctx context.Context, v4 bool, device ...str
devices := VirtualDeviceList(c.Val.(types.ArrayOfVirtualDevice).VirtualDevice)
for _, d := range devices {
if nic, ok := d.(types.BaseVirtualEthernetCard); ok {
mac := nic.GetVirtualEthernetCard().MacAddress
// Convert to lower so that e.g. 00:50:56:83:3A:5D is treated the
// same as 00:50:56:83:3a:5d
mac := strings.ToLower(nic.GetVirtualEthernetCard().MacAddress)
if mac == "" {
return false
}
@@ -285,6 +349,10 @@ func (v VirtualMachine) WaitForNetIP(ctx context.Context, v4 bool, device ...str
return true
})
if err != nil {
return nil, err
}
if len(device) != 0 {
// Only wait for specific NIC(s)
macs = make(map[string][]string)
@@ -304,7 +372,9 @@ func (v VirtualMachine) WaitForNetIP(ctx context.Context, v4 bool, device ...str
nics := c.Val.(types.ArrayOfGuestNicInfo).GuestNicInfo
for _, nic := range nics {
mac := nic.MacAddress
// Convert to lower so that e.g. 00:50:56:83:3A:5D is treated the
// same as 00:50:56:83:3a:5d
mac := strings.ToLower(nic.MacAddress)
if mac == "" || nic.IpConfig == nil {
continue
}
@@ -391,29 +461,33 @@ func (v VirtualMachine) ResourcePool(ctx context.Context) (*ResourcePool, error)
return NewResourcePool(v.c, *rp), nil
}
func diskFileOperation(op types.VirtualDeviceConfigSpecOperation, fop types.VirtualDeviceConfigSpecFileOperation, device types.BaseVirtualDevice) types.VirtualDeviceConfigSpecFileOperation {
if disk, ok := device.(*types.VirtualDisk); ok {
// Special case to attach an existing disk
if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 && disk.CapacityInBytes == 0 {
childDisk := false
if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
childDisk = b.Parent != nil
}
if !childDisk {
fop = "" // existing disk
}
}
return fop
}
return ""
}
func (v VirtualMachine) configureDevice(ctx context.Context, op types.VirtualDeviceConfigSpecOperation, fop types.VirtualDeviceConfigSpecFileOperation, devices ...types.BaseVirtualDevice) error {
spec := types.VirtualMachineConfigSpec{}
for _, device := range devices {
config := &types.VirtualDeviceConfigSpec{
Device: device,
Operation: op,
}
if disk, ok := device.(*types.VirtualDisk); ok {
config.FileOperation = fop
// Special case to attach an existing disk
if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 {
childDisk := false
if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
childDisk = b.Parent != nil
}
if !childDisk {
config.FileOperation = "" // existing disk
}
}
Device: device,
Operation: op,
FileOperation: diskFileOperation(op, fop, device),
}
spec.DeviceChange = append(spec.DeviceChange, config)
@@ -446,6 +520,41 @@ func (v VirtualMachine) RemoveDevice(ctx context.Context, keepFiles bool, device
return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationRemove, fop, device...)
}
// AttachDisk attaches the given disk to the VirtualMachine
func (v VirtualMachine) AttachDisk(ctx context.Context, id string, datastore *Datastore, controllerKey int32, unitNumber int32) error {
req := types.AttachDisk_Task{
This: v.Reference(),
DiskId: types.ID{Id: id},
Datastore: datastore.Reference(),
ControllerKey: controllerKey,
UnitNumber: &unitNumber,
}
res, err := methods.AttachDisk_Task(ctx, v.c, &req)
if err != nil {
return err
}
task := NewTask(v.c, res.Returnval)
return task.Wait(ctx)
}
// DetachDisk detaches the given disk from the VirtualMachine
func (v VirtualMachine) DetachDisk(ctx context.Context, id string) error {
req := types.DetachDisk_Task{
This: v.Reference(),
DiskId: types.ID{Id: id},
}
res, err := methods.DetachDisk_Task(ctx, v.c, &req)
if err != nil {
return err
}
task := NewTask(v.c, res.Returnval)
return task.Wait(ctx)
}
// BootOptions returns the VirtualMachine's config.bootOptions property.
func (v VirtualMachine) BootOptions(ctx context.Context) (*types.VirtualMachineBootOptions, error) {
var o mo.VirtualMachine
@@ -556,6 +665,63 @@ func (m snapshotMap) add(parent string, tree []types.VirtualMachineSnapshotTree)
}
}
// SnapshotSize calculates the size of a given snapshot in bytes. If the
// snapshot is current, disk files not associated with any parent snapshot are
// included in size calculations. This allows for measuring and including the
// growth from the last fixed snapshot to the present state.
func SnapshotSize(info types.ManagedObjectReference, parent *types.ManagedObjectReference, vmlayout *types.VirtualMachineFileLayoutEx, isCurrent bool) int {
var fileKeyList []int
var parentFiles []int
var allSnapshotFiles []int
diskFiles := extractDiskLayoutFiles(vmlayout.Disk)
for _, layout := range vmlayout.Snapshot {
diskLayout := extractDiskLayoutFiles(layout.Disk)
allSnapshotFiles = append(allSnapshotFiles, diskLayout...)
if layout.Key.Value == info.Value {
fileKeyList = append(fileKeyList, int(layout.DataKey)) // The .vmsn file
fileKeyList = append(fileKeyList, diskLayout...) // The .vmdk files
} else if parent != nil && layout.Key.Value == parent.Value {
parentFiles = append(parentFiles, diskLayout...)
}
}
for _, parentFile := range parentFiles {
removeKey(&fileKeyList, parentFile)
}
for _, file := range allSnapshotFiles {
removeKey(&diskFiles, file)
}
fileKeyMap := make(map[int]types.VirtualMachineFileLayoutExFileInfo)
for _, file := range vmlayout.File {
fileKeyMap[int(file.Key)] = file
}
size := 0
for _, fileKey := range fileKeyList {
file := fileKeyMap[fileKey]
if parent != nil ||
(file.Type != string(types.VirtualMachineFileLayoutExFileTypeDiskDescriptor) &&
file.Type != string(types.VirtualMachineFileLayoutExFileTypeDiskExtent)) {
size += int(file.Size)
}
}
if isCurrent {
for _, diskFile := range diskFiles {
file := fileKeyMap[diskFile]
size += int(file.Size)
}
}
return size
}
// FindSnapshot supports snapshot lookup by name, where name can be:
// 1) snapshot ManagedObjectReference.Value (unique)
// 2) snapshot name (may not be unique)
@@ -569,7 +735,7 @@ func (v VirtualMachine) FindSnapshot(ctx context.Context, name string) (*types.M
}
if o.Snapshot == nil || len(o.Snapshot.RootSnapshotList) == 0 {
return nil, errors.New("No snapshots for this VM")
return nil, errors.New("no snapshots for this VM")
}
m := make(snapshotMap)
@@ -832,6 +998,85 @@ func (v VirtualMachine) UUID(ctx context.Context) string {
if err != nil {
return ""
}
return o.Config.Uuid
if o.Config != nil {
return o.Config.Uuid
}
return ""
}
func (v VirtualMachine) QueryChangedDiskAreas(ctx context.Context, baseSnapshot, curSnapshot *types.ManagedObjectReference, disk *types.VirtualDisk, offset int64) (types.DiskChangeInfo, error) {
var noChange types.DiskChangeInfo
var err error
if offset > disk.CapacityInBytes {
return noChange, fmt.Errorf("offset is greater than the disk size (%#x and %#x)", offset, disk.CapacityInBytes)
} else if offset == disk.CapacityInBytes {
return types.DiskChangeInfo{StartOffset: offset, Length: 0}, nil
}
var b mo.VirtualMachineSnapshot
err = v.Properties(ctx, baseSnapshot.Reference(), []string{"config.hardware"}, &b)
if err != nil {
return noChange, fmt.Errorf("failed to fetch config.hardware of snapshot %s: %s", baseSnapshot, err)
}
var changeId *string
for _, vd := range b.Config.Hardware.Device {
d := vd.GetVirtualDevice()
if d.Key != disk.Key {
continue
}
// As per VDDK programming guide, these are the four types of disks
// that support CBT, see "Gathering Changed Block Information".
if b, ok := d.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
changeId = &b.ChangeId
break
}
if b, ok := d.Backing.(*types.VirtualDiskSparseVer2BackingInfo); ok {
changeId = &b.ChangeId
break
}
if b, ok := d.Backing.(*types.VirtualDiskRawDiskMappingVer1BackingInfo); ok {
changeId = &b.ChangeId
break
}
if b, ok := d.Backing.(*types.VirtualDiskRawDiskVer2BackingInfo); ok {
changeId = &b.ChangeId
break
}
return noChange, fmt.Errorf("disk %d has backing info without .ChangeId: %t", disk.Key, d.Backing)
}
if changeId == nil || *changeId == "" {
return noChange, fmt.Errorf("CBT is not enabled on disk %d", disk.Key)
}
req := types.QueryChangedDiskAreas{
This: v.Reference(),
Snapshot: curSnapshot,
DeviceKey: disk.Key,
StartOffset: offset,
ChangeId: *changeId,
}
res, err := methods.QueryChangedDiskAreas(ctx, v.Client(), &req)
if err != nil {
return noChange, err
}
return res.Returnval, nil
}
// ExportSnapshot exports all VMDK-files up to (but not including) a specified snapshot. This
// is useful when exporting a running VM.
func (v *VirtualMachine) ExportSnapshot(ctx context.Context, snapshot *types.ManagedObjectReference) (*nfc.Lease, error) {
req := types.ExportSnapshot{
This: *snapshot,
}
resp, err := methods.ExportSnapshot(ctx, v.Client(), &req)
if err != nil {
return nil, err
}
return nfc.NewLease(v.c, resp.Returnval), nil
}

View File

@@ -19,3 +19,7 @@ package object
type VmwareDistributedVirtualSwitch struct {
DistributedVirtualSwitch
}
func (s VmwareDistributedVirtualSwitch) GetInventoryPath() string {
return s.InventoryPath
}

128
vendor/github.com/vmware/govmomi/ovf/cim.go generated vendored Normal file
View File

@@ -0,0 +1,128 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"github.com/vmware/govmomi/vim25/types"
)
/*
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_VirtualSystemSettingData.xsd
*/
type CIMVirtualSystemSettingData struct {
ElementName string `xml:"ElementName"`
InstanceID string `xml:"InstanceID"`
AutomaticRecoveryAction *uint8 `xml:"AutomaticRecoveryAction"`
AutomaticShutdownAction *uint8 `xml:"AutomaticShutdownAction"`
AutomaticStartupAction *uint8 `xml:"AutomaticStartupAction"`
AutomaticStartupActionDelay *string `xml:"AutomaticStartupActionDelay>Interval"`
AutomaticStartupActionSequenceNumber *uint16 `xml:"AutomaticStartupActionSequenceNumber"`
Caption *string `xml:"Caption"`
ConfigurationDataRoot *string `xml:"ConfigurationDataRoot"`
ConfigurationFile *string `xml:"ConfigurationFile"`
ConfigurationID *string `xml:"ConfigurationID"`
CreationTime *string `xml:"CreationTime"`
Description *string `xml:"Description"`
LogDataRoot *string `xml:"LogDataRoot"`
Notes []string `xml:"Notes"`
RecoveryFile *string `xml:"RecoveryFile"`
SnapshotDataRoot *string `xml:"SnapshotDataRoot"`
SuspendDataRoot *string `xml:"SuspendDataRoot"`
SwapFileDataRoot *string `xml:"SwapFileDataRoot"`
VirtualSystemIdentifier *string `xml:"VirtualSystemIdentifier"`
VirtualSystemType *string `xml:"VirtualSystemType"`
}
/*
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_ResourceAllocationSettingData.xsd
*/
type CIMResourceAllocationSettingData struct {
ElementName string `xml:"ElementName"`
InstanceID string `xml:"InstanceID"`
ResourceType *uint16 `xml:"ResourceType"`
OtherResourceType *string `xml:"OtherResourceType"`
ResourceSubType *string `xml:"ResourceSubType"`
AddressOnParent *string `xml:"AddressOnParent"`
Address *string `xml:"Address"`
AllocationUnits *string `xml:"AllocationUnits"`
AutomaticAllocation *bool `xml:"AutomaticAllocation"`
AutomaticDeallocation *bool `xml:"AutomaticDeallocation"`
Caption *string `xml:"Caption"`
Connection []string `xml:"Connection"`
ConsumerVisibility *uint16 `xml:"ConsumerVisibility"`
Description *string `xml:"Description"`
HostResource []string `xml:"HostResource"`
Limit *uint64 `xml:"Limit"`
MappingBehavior *uint `xml:"MappingBehavior"`
Parent *string `xml:"Parent"`
PoolID *string `xml:"PoolID"`
Reservation *uint64 `xml:"Reservation"`
VirtualQuantity *uint `xml:"VirtualQuantity"`
VirtualQuantityUnits *string `xml:"VirtualQuantityUnits"`
Weight *uint `xml:"Weight"`
}
/*
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_StorageAllocationSettingData.xsd
*/
type CIMStorageAllocationSettingData struct {
ElementName string `xml:"ElementName"`
InstanceID string `xml:"InstanceID"`
ResourceType *uint16 `xml:"ResourceType"`
OtherResourceType *string `xml:"OtherResourceType"`
ResourceSubType *string `xml:"ResourceSubType"`
Access *uint16 `xml:"Access"`
Address *string `xml:"Address"`
AddressOnParent *string `xml:"AddressOnParent"`
AllocationUnits *string `xml:"AllocationUnits"`
AutomaticAllocation *bool `xml:"AutomaticAllocation"`
AutomaticDeallocation *bool `xml:"AutomaticDeallocation"`
Caption *string `xml:"Caption"`
ChangeableType *uint16 `xml:"ChangeableType"`
ComponentSetting []types.AnyType `xml:"ComponentSetting"`
ConfigurationName *string `xml:"ConfigurationName"`
Connection []string `xml:"Connection"`
ConsumerVisibility *uint16 `xml:"ConsumerVisibility"`
Description *string `xml:"Description"`
Generation *uint64 `xml:"Generation"`
HostExtentName *string `xml:"HostExtentName"`
HostExtentNameFormat *uint16 `xml:"HostExtentNameFormat"`
HostExtentNameNamespace *uint16 `xml:"HostExtentNameNamespace"`
HostExtentStartingAddress *uint64 `xml:"HostExtentStartingAddress"`
HostResource []string `xml:"HostResource"`
HostResourceBlockSize *uint64 `xml:"HostResourceBlockSize"`
Limit *uint64 `xml:"Limit"`
MappingBehavior *uint `xml:"MappingBehavior"`
OtherHostExtentNameFormat *string `xml:"OtherHostExtentNameFormat"`
OtherHostExtentNameNamespace *string `xml:"OtherHostExtentNameNamespace"`
Parent *string `xml:"Parent"`
PoolID *string `xml:"PoolID"`
Reservation *uint64 `xml:"Reservation"`
SoID *string `xml:"SoID"`
SoOrgID *string `xml:"SoOrgID"`
VirtualQuantity *uint `xml:"VirtualQuantity"`
VirtualQuantityUnits *string `xml:"VirtualQuantityUnits"`
VirtualResourceBlockSize *uint64 `xml:"VirtualResourceBlockSize"`
Weight *uint `xml:"Weight"`
}

25
vendor/github.com/vmware/govmomi/ovf/doc.go generated vendored Normal file
View File

@@ -0,0 +1,25 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package ovf provides functionality to unmarshal and inspect the structure
of an OVF file. It is not a complete implementation of the specification and
is intended to be used to import virtual infrastructure into vSphere.
For a complete specification of the OVF standard, refer to:
https://www.dmtf.org/sites/default/files/standards/documents/DSP0243_2.1.0.pdf
*/
package ovf

99
vendor/github.com/vmware/govmomi/ovf/env.go generated vendored Normal file
View File

@@ -0,0 +1,99 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"bytes"
"fmt"
"github.com/vmware/govmomi/vim25/xml"
)
const (
ovfEnvHeader = `<Environment
xmlns="http://schemas.dmtf.org/ovf/environment/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
xmlns:ve="http://www.vmware.com/schema/ovfenv"
oe:id=""
ve:esxId="%s">`
ovfEnvPlatformSection = `<PlatformSection>
<Kind>%s</Kind>
<Version>%s</Version>
<Vendor>%s</Vendor>
<Locale>%s</Locale>
</PlatformSection>`
ovfEnvPropertyHeader = `<PropertySection>`
ovfEnvPropertyEntry = `<Property oe:key="%s" oe:value="%s"/>`
ovfEnvPropertyFooter = `</PropertySection>`
ovfEnvFooter = `</Environment>`
)
type Env struct {
XMLName xml.Name `xml:"http://schemas.dmtf.org/ovf/environment/1 Environment"`
ID string `xml:"id,attr"`
EsxID string `xml:"http://www.vmware.com/schema/ovfenv esxId,attr"`
Platform *PlatformSection `xml:"PlatformSection"`
Property *PropertySection `xml:"PropertySection"`
}
type PlatformSection struct {
Kind string `xml:"Kind"`
Version string `xml:"Version"`
Vendor string `xml:"Vendor"`
Locale string `xml:"Locale"`
}
type PropertySection struct {
Properties []EnvProperty `xml:"Property"`
}
type EnvProperty struct {
Key string `xml:"key,attr"`
Value string `xml:"value,attr"`
}
// Marshal marshals Env to xml by using xml.Marshal.
func (e Env) Marshal() (string, error) {
x, err := xml.Marshal(e)
if err != nil {
return "", err
}
return fmt.Sprintf("%s%s", xml.Header, x), nil
}
// MarshalManual manually marshals Env to xml suitable for a vApp guest.
// It exists to overcome the lack of expressiveness in Go's XML namespaces.
func (e Env) MarshalManual() string {
var buffer bytes.Buffer
buffer.WriteString(xml.Header)
buffer.WriteString(fmt.Sprintf(ovfEnvHeader, e.EsxID))
buffer.WriteString(fmt.Sprintf(ovfEnvPlatformSection, e.Platform.Kind, e.Platform.Version, e.Platform.Vendor, e.Platform.Locale))
buffer.WriteString(fmt.Sprint(ovfEnvPropertyHeader))
for _, p := range e.Property.Properties {
buffer.WriteString(fmt.Sprintf(ovfEnvPropertyEntry, p.Key, p.Value))
}
buffer.WriteString(fmt.Sprint(ovfEnvPropertyFooter))
buffer.WriteString(fmt.Sprint(ovfEnvFooter))
return buffer.String()
}

208
vendor/github.com/vmware/govmomi/ovf/envelope.go generated vendored Normal file
View File

@@ -0,0 +1,208 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
type Envelope struct {
References []File `xml:"References>File"`
// Package level meta-data
Annotation *AnnotationSection `xml:"AnnotationSection"`
Product *ProductSection `xml:"ProductSection"`
Network *NetworkSection `xml:"NetworkSection"`
Disk *DiskSection `xml:"DiskSection"`
OperatingSystem *OperatingSystemSection `xml:"OperatingSystemSection"`
Eula *EulaSection `xml:"EulaSection"`
VirtualHardware *VirtualHardwareSection `xml:"VirtualHardwareSection"`
ResourceAllocation *ResourceAllocationSection `xml:"ResourceAllocationSection"`
DeploymentOption *DeploymentOptionSection `xml:"DeploymentOptionSection"`
// Content: A VirtualSystem or a VirtualSystemCollection
VirtualSystem *VirtualSystem `xml:"VirtualSystem"`
}
type VirtualSystem struct {
Content
Annotation []AnnotationSection `xml:"AnnotationSection"`
Product []ProductSection `xml:"ProductSection"`
OperatingSystem []OperatingSystemSection `xml:"OperatingSystemSection"`
Eula []EulaSection `xml:"EulaSection"`
VirtualHardware []VirtualHardwareSection `xml:"VirtualHardwareSection"`
}
type File struct {
ID string `xml:"id,attr"`
Href string `xml:"href,attr"`
Size uint `xml:"size,attr"`
Compression *string `xml:"compression,attr"`
ChunkSize *int `xml:"chunkSize,attr"`
}
type Content struct {
ID string `xml:"id,attr"`
Info string `xml:"Info"`
Name *string `xml:"Name"`
}
type Section struct {
Required *bool `xml:"required,attr"`
Info string `xml:"Info"`
}
type AnnotationSection struct {
Section
Annotation string `xml:"Annotation"`
}
type ProductSection struct {
Section
Class *string `xml:"class,attr"`
Instance *string `xml:"instance,attr"`
Product string `xml:"Product"`
Vendor string `xml:"Vendor"`
Version string `xml:"Version"`
FullVersion string `xml:"FullVersion"`
ProductURL string `xml:"ProductUrl"`
VendorURL string `xml:"VendorUrl"`
AppURL string `xml:"AppUrl"`
Property []Property `xml:"Property"`
}
type Property struct {
Key string `xml:"key,attr"`
Type string `xml:"type,attr"`
Qualifiers *string `xml:"qualifiers,attr"`
UserConfigurable *bool `xml:"userConfigurable,attr"`
Default *string `xml:"value,attr"`
Password *bool `xml:"password,attr"`
Label *string `xml:"Label"`
Description *string `xml:"Description"`
Values []PropertyConfigurationValue `xml:"Value"`
}
type PropertyConfigurationValue struct {
Value string `xml:"value,attr"`
Configuration *string `xml:"configuration,attr"`
}
type NetworkSection struct {
Section
Networks []Network `xml:"Network"`
}
type Network struct {
Name string `xml:"name,attr"`
Description string `xml:"Description"`
}
type DiskSection struct {
Section
Disks []VirtualDiskDesc `xml:"Disk"`
}
type VirtualDiskDesc struct {
DiskID string `xml:"diskId,attr"`
FileRef *string `xml:"fileRef,attr"`
Capacity string `xml:"capacity,attr"`
CapacityAllocationUnits *string `xml:"capacityAllocationUnits,attr"`
Format *string `xml:"format,attr"`
PopulatedSize *int `xml:"populatedSize,attr"`
ParentRef *string `xml:"parentRef,attr"`
}
type OperatingSystemSection struct {
Section
ID int16 `xml:"id,attr"`
Version *string `xml:"version,attr"`
OSType *string `xml:"osType,attr"`
Description *string `xml:"Description"`
}
type EulaSection struct {
Section
License string `xml:"License"`
}
type Config struct {
Required *bool `xml:"required,attr"`
Key string `xml:"key,attr"`
Value string `xml:"value,attr"`
}
type VirtualHardwareSection struct {
Section
ID *string `xml:"id,attr"`
Transport *string `xml:"transport,attr"`
System *VirtualSystemSettingData `xml:"System"`
Item []ResourceAllocationSettingData `xml:"Item"`
StorageItem []StorageAllocationSettingData `xml:"StorageItem"`
Config []Config `xml:"Config"`
ExtraConfig []Config `xml:"ExtraConfig"`
}
type VirtualSystemSettingData struct {
CIMVirtualSystemSettingData
}
type ResourceAllocationSettingData struct {
CIMResourceAllocationSettingData
Required *bool `xml:"required,attr"`
Configuration *string `xml:"configuration,attr"`
Bound *string `xml:"bound,attr"`
}
type StorageAllocationSettingData struct {
CIMStorageAllocationSettingData
Required *bool `xml:"required,attr"`
Configuration *string `xml:"configuration,attr"`
Bound *string `xml:"bound,attr"`
}
type ResourceAllocationSection struct {
Section
Item []ResourceAllocationSettingData `xml:"Item"`
}
type DeploymentOptionSection struct {
Section
Configuration []DeploymentOptionConfiguration `xml:"Configuration"`
}
type DeploymentOptionConfiguration struct {
ID string `xml:"id,attr"`
Default *bool `xml:"default,attr"`
Label string `xml:"Label"`
Description string `xml:"Description"`
}

103
vendor/github.com/vmware/govmomi/ovf/manager.go generated vendored Normal file
View File

@@ -0,0 +1,103 @@
/*
Copyright (c) 2015-2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"context"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type Manager struct {
types.ManagedObjectReference
c *vim25.Client
}
func NewManager(c *vim25.Client) *Manager {
return &Manager{*c.ServiceContent.OvfManager, c}
}
// CreateDescriptor wraps methods.CreateDescriptor
func (m *Manager) CreateDescriptor(ctx context.Context, obj mo.Reference, cdp types.OvfCreateDescriptorParams) (*types.OvfCreateDescriptorResult, error) {
req := types.CreateDescriptor{
This: m.Reference(),
Obj: obj.Reference(),
Cdp: cdp,
}
res, err := methods.CreateDescriptor(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}
// CreateImportSpec wraps methods.CreateImportSpec
func (m *Manager) CreateImportSpec(ctx context.Context, ovfDescriptor string, resourcePool mo.Reference, datastore mo.Reference, cisp types.OvfCreateImportSpecParams) (*types.OvfCreateImportSpecResult, error) {
req := types.CreateImportSpec{
This: m.Reference(),
OvfDescriptor: ovfDescriptor,
ResourcePool: resourcePool.Reference(),
Datastore: datastore.Reference(),
Cisp: cisp,
}
res, err := methods.CreateImportSpec(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}
// ParseDescriptor wraps methods.ParseDescriptor
func (m *Manager) ParseDescriptor(ctx context.Context, ovfDescriptor string, pdp types.OvfParseDescriptorParams) (*types.OvfParseDescriptorResult, error) {
req := types.ParseDescriptor{
This: m.Reference(),
OvfDescriptor: ovfDescriptor,
Pdp: pdp,
}
res, err := methods.ParseDescriptor(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}
// ValidateHost wraps methods.ValidateHost
func (m *Manager) ValidateHost(ctx context.Context, ovfDescriptor string, host mo.Reference, vhp types.OvfValidateHostParams) (*types.OvfValidateHostResult, error) {
req := types.ValidateHost{
This: m.Reference(),
OvfDescriptor: ovfDescriptor,
Host: host.Reference(),
Vhp: vhp,
}
res, err := methods.ValidateHost(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}

View File

@@ -1,7 +1,5 @@
//+build !windows
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -16,23 +14,22 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
package ovf
import "syscall"
import (
"io"
func (ds *Datastore) stat() error {
info := ds.Info.GetDatastoreInfo()
var stat syscall.Statfs_t
"github.com/vmware/govmomi/vim25/xml"
)
err := syscall.Statfs(info.Url, &stat)
func Unmarshal(r io.Reader) (*Envelope, error) {
var e Envelope
dec := xml.NewDecoder(r)
err := dec.Decode(&e)
if err != nil {
return err
return nil, err
}
info.FreeSpace = int64(stat.Bfree * uint64(stat.Bsize))
ds.Summary.FreeSpace = info.FreeSpace
ds.Summary.Capacity = int64(stat.Blocks * uint64(stat.Bsize))
return nil
return &e, nil
}

View File

@@ -43,6 +43,8 @@ type Client struct {
*soap.Client
ServiceContent types.PbmServiceInstanceContent
RoundTripper soap.RoundTripper
}
func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
@@ -57,7 +59,12 @@ func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
return nil, err
}
return &Client{sc, res.Returnval}, nil
return &Client{sc, res.Returnval, sc}, nil
}
// RoundTrip dispatches to the RoundTripper field.
func (c *Client) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
return c.RoundTripper.RoundTrip(ctx, req, res)
}
func (c *Client) QueryProfile(ctx context.Context, rtype types.PbmProfileResourceType, category string) ([]types.PbmProfileId, error) {
@@ -224,3 +231,57 @@ func (c *Client) ProfileIDByName(ctx context.Context, profileName string) (strin
}
return "", fmt.Errorf("no pbm profile found with name: %q", profileName)
}
func (c *Client) FetchCapabilityMetadata(ctx context.Context, rtype *types.PbmProfileResourceType, vendorUuid string) ([]types.PbmCapabilityMetadataPerCategory, error) {
req := types.PbmFetchCapabilityMetadata{
This: c.ServiceContent.ProfileManager,
ResourceType: rtype,
VendorUuid: vendorUuid,
}
res, err := methods.PbmFetchCapabilityMetadata(ctx, c, &req)
if err != nil {
return nil, err
}
return res.Returnval, nil
}
func (c *Client) FetchComplianceResult(ctx context.Context, entities []types.PbmServerObjectRef) ([]types.PbmComplianceResult, error) {
req := types.PbmFetchComplianceResult{
This: c.ServiceContent.ComplianceManager,
Entities: entities,
}
res, err := methods.PbmFetchComplianceResult(ctx, c, &req)
if err != nil {
return nil, err
}
return res.Returnval, nil
}
// GetProfileNameByID gets storage profile name by ID
func (c *Client) GetProfileNameByID(ctx context.Context, profileID string) (string, error) {
resourceType := types.PbmProfileResourceType{
ResourceType: string(types.PbmProfileResourceTypeEnumSTORAGE),
}
category := types.PbmProfileCategoryEnumREQUIREMENT
ids, err := c.QueryProfile(ctx, resourceType, string(category))
if err != nil {
return "", err
}
profiles, err := c.RetrieveContent(ctx, ids)
if err != nil {
return "", err
}
for i := range profiles {
profile := profiles[i].GetPbmProfile()
if profile.ProfileId.UniqueId == profileID {
return profile.Name, nil
}
}
return "", fmt.Errorf("no pbm profile found with id: %q", profileID)
}

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Copyright (c) 2014-2022 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.

View File

@@ -27,6 +27,7 @@ import (
// A struct to capture pbm create spec details.
type CapabilityProfileCreateSpec struct {
Name string
SubProfileName string
Description string
Category string
CapabilityList []Capability
@@ -64,6 +65,7 @@ func CreateCapabilityProfileSpec(pbmCreateSpec CapabilityProfileCreateSpec) (*ty
SubProfiles: []types.PbmCapabilitySubProfile{
types.PbmCapabilitySubProfile{
Capability: capabilities,
Name: pbmCreateSpec.SubProfileName,
},
},
},

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Copyright (c) 2014-2022 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -22,6 +22,18 @@ import (
"github.com/vmware/govmomi/vim25/types"
)
type PbmAssociateAndApplyPolicyStatusPolicyStatus string
const (
PbmAssociateAndApplyPolicyStatusPolicyStatusSuccess = PbmAssociateAndApplyPolicyStatusPolicyStatus("success")
PbmAssociateAndApplyPolicyStatusPolicyStatusFailed = PbmAssociateAndApplyPolicyStatusPolicyStatus("failed")
PbmAssociateAndApplyPolicyStatusPolicyStatusInvalid = PbmAssociateAndApplyPolicyStatusPolicyStatus("invalid")
)
func init() {
types.Add("pbm:PbmAssociateAndApplyPolicyStatusPolicyStatus", reflect.TypeOf((*PbmAssociateAndApplyPolicyStatusPolicyStatus)(nil)).Elem())
}
type PbmBuiltinGenericType string
const (
@@ -104,6 +116,30 @@ func init() {
types.Add("pbm:PbmComplianceStatus", reflect.TypeOf((*PbmComplianceStatus)(nil)).Elem())
}
type PbmDebugManagerKeystoreName string
const (
PbmDebugManagerKeystoreNameSMS = PbmDebugManagerKeystoreName("SMS")
PbmDebugManagerKeystoreNameTRUSTED_ROOTS = PbmDebugManagerKeystoreName("TRUSTED_ROOTS")
)
func init() {
types.Add("pbm:PbmDebugManagerKeystoreName", reflect.TypeOf((*PbmDebugManagerKeystoreName)(nil)).Elem())
}
type PbmHealthStatusForEntity string
const (
PbmHealthStatusForEntityRed = PbmHealthStatusForEntity("red")
PbmHealthStatusForEntityYellow = PbmHealthStatusForEntity("yellow")
PbmHealthStatusForEntityGreen = PbmHealthStatusForEntity("green")
PbmHealthStatusForEntityUnknown = PbmHealthStatusForEntity("unknown")
)
func init() {
types.Add("pbm:PbmHealthStatusForEntity", reflect.TypeOf((*PbmHealthStatusForEntity)(nil)).Elem())
}
type PbmIofilterInfoFilterType string
const (
@@ -131,12 +167,42 @@ const (
PbmLineOfServiceInfoLineOfServiceEnumPERSISTENCE = PbmLineOfServiceInfoLineOfServiceEnum("PERSISTENCE")
PbmLineOfServiceInfoLineOfServiceEnumDATA_PROVIDER = PbmLineOfServiceInfoLineOfServiceEnum("DATA_PROVIDER")
PbmLineOfServiceInfoLineOfServiceEnumDATASTORE_IO_CONTROL = PbmLineOfServiceInfoLineOfServiceEnum("DATASTORE_IO_CONTROL")
PbmLineOfServiceInfoLineOfServiceEnumDATA_PROTECTION = PbmLineOfServiceInfoLineOfServiceEnum("DATA_PROTECTION")
)
func init() {
types.Add("pbm:PbmLineOfServiceInfoLineOfServiceEnum", reflect.TypeOf((*PbmLineOfServiceInfoLineOfServiceEnum)(nil)).Elem())
}
type PbmLoggingConfigurationComponent string
const (
PbmLoggingConfigurationComponentPbm = PbmLoggingConfigurationComponent("pbm")
PbmLoggingConfigurationComponentVslm = PbmLoggingConfigurationComponent("vslm")
PbmLoggingConfigurationComponentSms = PbmLoggingConfigurationComponent("sms")
PbmLoggingConfigurationComponentSpbm = PbmLoggingConfigurationComponent("spbm")
PbmLoggingConfigurationComponentSps = PbmLoggingConfigurationComponent("sps")
PbmLoggingConfigurationComponentHttpclient_header = PbmLoggingConfigurationComponent("httpclient_header")
PbmLoggingConfigurationComponentHttpclient_content = PbmLoggingConfigurationComponent("httpclient_content")
PbmLoggingConfigurationComponentVmomi = PbmLoggingConfigurationComponent("vmomi")
)
func init() {
types.Add("pbm:PbmLoggingConfigurationComponent", reflect.TypeOf((*PbmLoggingConfigurationComponent)(nil)).Elem())
}
type PbmLoggingConfigurationLogLevel string
const (
PbmLoggingConfigurationLogLevelINFO = PbmLoggingConfigurationLogLevel("INFO")
PbmLoggingConfigurationLogLevelDEBUG = PbmLoggingConfigurationLogLevel("DEBUG")
PbmLoggingConfigurationLogLevelTRACE = PbmLoggingConfigurationLogLevel("TRACE")
)
func init() {
types.Add("pbm:PbmLoggingConfigurationLogLevel", reflect.TypeOf((*PbmLoggingConfigurationLogLevel)(nil)).Elem())
}
type PbmObjectType string
const (
@@ -145,6 +211,8 @@ const (
PbmObjectTypeVirtualDiskId = PbmObjectType("virtualDiskId")
PbmObjectTypeVirtualDiskUUID = PbmObjectType("virtualDiskUUID")
PbmObjectTypeDatastore = PbmObjectType("datastore")
PbmObjectTypeVsanObjectId = PbmObjectType("vsanObjectId")
PbmObjectTypeFileShareId = PbmObjectType("fileShareId")
PbmObjectTypeUnknown = PbmObjectType("unknown")
)
@@ -166,6 +234,18 @@ func init() {
types.Add("pbm:PbmOperation", reflect.TypeOf((*PbmOperation)(nil)).Elem())
}
type PbmPolicyAssociationVolumeAllocationType string
const (
PbmPolicyAssociationVolumeAllocationTypeFullyInitialized = PbmPolicyAssociationVolumeAllocationType("FullyInitialized")
PbmPolicyAssociationVolumeAllocationTypeReserveSpace = PbmPolicyAssociationVolumeAllocationType("ReserveSpace")
PbmPolicyAssociationVolumeAllocationTypeConserveSpaceWhenPossible = PbmPolicyAssociationVolumeAllocationType("ConserveSpaceWhenPossible")
)
func init() {
types.Add("pbm:PbmPolicyAssociationVolumeAllocationType", reflect.TypeOf((*PbmPolicyAssociationVolumeAllocationType)(nil)).Elem())
}
type PbmProfileCategoryEnum string
const (
@@ -191,9 +271,10 @@ func init() {
type PbmSystemCreatedProfileType string
const (
PbmSystemCreatedProfileTypeVsanDefaultProfile = PbmSystemCreatedProfileType("VsanDefaultProfile")
PbmSystemCreatedProfileTypeVVolDefaultProfile = PbmSystemCreatedProfileType("VVolDefaultProfile")
PbmSystemCreatedProfileTypePmemDefaultProfile = PbmSystemCreatedProfileType("PmemDefaultProfile")
PbmSystemCreatedProfileTypeVsanDefaultProfile = PbmSystemCreatedProfileType("VsanDefaultProfile")
PbmSystemCreatedProfileTypeVVolDefaultProfile = PbmSystemCreatedProfileType("VVolDefaultProfile")
PbmSystemCreatedProfileTypePmemDefaultProfile = PbmSystemCreatedProfileType("PmemDefaultProfile")
PbmSystemCreatedProfileTypeVsanMaxDefaultProfile = PbmSystemCreatedProfileType("VsanMaxDefaultProfile")
)
func init() {

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Copyright (c) 2014-2022 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.

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Copyright (c) 2014-2022 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.
@@ -909,6 +909,17 @@ func init() {
types.Add("pbm:PbmFaultInvalidLoginFault", reflect.TypeOf((*PbmFaultInvalidLoginFault)(nil)).Elem())
}
type PbmFaultNoPermissionEntityPrivileges struct {
types.DynamicData
ProfileId *PbmProfileId `xml:"profileId,omitempty"`
PrivilegeIds []string `xml:"privilegeIds,omitempty"`
}
func init() {
types.Add("pbm:PbmFaultNoPermissionEntityPrivileges", reflect.TypeOf((*PbmFaultNoPermissionEntityPrivileges)(nil)).Elem())
}
type PbmFaultNotFound struct {
PbmFault
}
@@ -997,6 +1008,17 @@ type PbmFetchComplianceResultResponse struct {
Returnval []PbmComplianceResult `xml:"returnval,omitempty"`
}
type PbmFetchEntityHealthStatusSpec struct {
types.DynamicData
ObjectRef PbmServerObjectRef `xml:"objectRef"`
BackingId string `xml:"backingId,omitempty"`
}
func init() {
types.Add("pbm:PbmFetchEntityHealthStatusSpec", reflect.TypeOf((*PbmFetchEntityHealthStatusSpec)(nil)).Elem())
}
type PbmFetchResourceType PbmFetchResourceTypeRequestType
func init() {
@@ -1114,6 +1136,17 @@ func init() {
types.Add("pbm:PbmLineOfServiceInfo", reflect.TypeOf((*PbmLineOfServiceInfo)(nil)).Elem())
}
type PbmLoggingConfiguration struct {
types.DynamicData
Component string `xml:"component"`
LogLevel string `xml:"logLevel"`
}
func init() {
types.Add("pbm:PbmLoggingConfiguration", reflect.TypeOf((*PbmLoggingConfiguration)(nil)).Elem())
}
type PbmNonExistentHubs struct {
PbmFault

View File

@@ -1,3 +1,6 @@
# Copyright (c) 2021 VMware, Inc. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
ifneq (,$(strip $(GOOS)))
ifeq (,$(strip $(GOARCH)))
GOARCH := $(shell go env | grep GOARCH | awk -F= '{print $$2}' | tr -d '"')
@@ -30,7 +33,7 @@ $(PROGRAM):
CGO_ENABLED=0 go build -a $(BUILD_ARGS) -o $@
install:
CGO_ENABLED=0 go install $(BUILD_ARGS)
CGO_ENABLED=0 go install -v $(BUILD_ARGS)
ifneq (,$(strip $(BUILD_OS)))
ifneq (,$(strip $(BUILD_ARCH)))

View File

@@ -97,10 +97,16 @@ func (p *Collector) CreateFilter(ctx context.Context, req types.CreateFilter) er
return nil
}
func (p *Collector) WaitForUpdates(ctx context.Context, v string) (*types.UpdateSet, error) {
func (p *Collector) WaitForUpdates(ctx context.Context, version string, opts ...*types.WaitOptions) (*types.UpdateSet, error) {
req := types.WaitForUpdatesEx{
This: p.Reference(),
Version: v,
Version: version,
}
if len(opts) == 1 {
req.Options = opts[0]
} else if len(opts) > 1 {
panic("only one option may be specified")
}
res, err := methods.WaitForUpdatesEx(ctx, p.roundTripper, &req)
@@ -143,7 +149,7 @@ func (p *Collector) Retrieve(ctx context.Context, objs []types.ManagedObjectRefe
spec := types.PropertySpec{
Type: obj.Type,
}
if ps == nil {
if len(ps) == 0 {
spec.All = types.NewBool(true)
} else {
spec.PathSet = ps
@@ -179,7 +185,7 @@ func (p *Collector) Retrieve(ctx context.Context, objs []types.ManagedObjectRefe
return nil
}
return mo.LoadRetrievePropertiesResponse(res, dst)
return mo.LoadObjectContent(res.Returnval, dst)
}
// RetrieveWithFilter populates dst as Retrieve does, but only for entities matching the given filter.

View File

@@ -42,6 +42,9 @@ func (f Filter) Keys() []string {
// MatchProperty returns true if a Filter entry matches the given prop.
func (f Filter) MatchProperty(prop types.DynamicProperty) bool {
if prop.Val == nil {
return false
}
match, ok := f[prop.Name]
if !ok {
return false
@@ -74,7 +77,7 @@ func (f Filter) MatchProperty(prop types.DynamicProperty) bool {
}
// convert if we can
switch prop.Val.(type) {
switch val := prop.Val.(type) {
case bool:
match, _ = strconv.ParseBool(s)
case int16:
@@ -91,7 +94,9 @@ func (f Filter) MatchProperty(prop types.DynamicProperty) bool {
case float64:
match, _ = strconv.ParseFloat(s, 64)
case fmt.Stringer:
prop.Val = prop.Val.(fmt.Stringer).String()
prop.Val = val.String()
case *types.CustomFieldStringValue:
prop.Val = fmt.Sprintf("%d:%s", val.Key, val.Value)
default:
if ptype.Kind() != reflect.String {
return false
@@ -125,7 +130,7 @@ func (f Filter) MatchPropertyList(props []types.DynamicProperty) bool {
return len(f) == len(props) // false if a property such as VM "guest" is unset
}
// MatchObjectContent returns a list of ObjectContent.Obj where the ObjectContent.PropSet matches the Filter.
// MatchObjectContent returns a list of ObjectContent.Obj where the ObjectContent.PropSet matches all properties the Filter.
func (f Filter) MatchObjectContent(objects []types.ObjectContent) []types.ManagedObjectReference {
var refs []types.ManagedObjectReference
@@ -137,3 +142,27 @@ func (f Filter) MatchObjectContent(objects []types.ObjectContent) []types.Manage
return refs
}
// MatchAnyPropertyList returns true if any given props match the Filter.
func (f Filter) MatchAnyPropertyList(props []types.DynamicProperty) bool {
for _, p := range props {
if f.MatchProperty(p) {
return true
}
}
return false
}
// MatchAnyObjectContent returns a list of ObjectContent.Obj where the ObjectContent.PropSet matches any property in the Filter.
func (f Filter) MatchAnyObjectContent(objects []types.ObjectContent) []types.ManagedObjectReference {
var refs []types.ManagedObjectReference
for _, o := range objects {
if f.MatchAnyPropertyList(o.PropSet) {
refs = append(refs, o.Obj)
}
}
return refs
}

View File

@@ -20,13 +20,16 @@ import (
"context"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
// WaitFilter provides helpers to construct a types.CreateFilter for use with property.Wait
type WaitFilter struct {
types.CreateFilter
Options *types.WaitOptions
Options *types.WaitOptions
PropagateMissing bool
Truncated bool
}
// Add a new ObjectSpec and PropertySpec to the WaitFilter
@@ -81,6 +84,8 @@ func Wait(ctx context.Context, c *Collector, obj types.ManagedObjectReference, p
// The newly created collector is destroyed before this function returns (both
// in case of success or error).
//
// By default, ObjectUpdate.MissingSet faults are not propagated to the returned error,
// set WaitFilter.PropagateMissing=true to enable MissingSet fault propagation.
func WaitForUpdates(ctx context.Context, c *Collector, filter *WaitFilter, f func([]types.ObjectUpdate) bool) error {
p, err := c.Create(ctx)
if err != nil {
@@ -89,7 +94,9 @@ func WaitForUpdates(ctx context.Context, c *Collector, filter *WaitFilter, f fun
// Attempt to destroy the collector using the background context, as the
// specified context may have timed out or have been canceled.
defer p.Destroy(context.Background())
defer func() {
_ = p.Destroy(context.Background())
}()
err = p.CreateFilter(ctx, filter.CreateFilter)
if err != nil {
@@ -121,8 +128,21 @@ func WaitForUpdates(ctx context.Context, c *Collector, filter *WaitFilter, f fun
}
req.Version = set.Version
filter.Truncated = false
if set.Truncated != nil {
filter.Truncated = *set.Truncated
}
for _, fs := range set.FilterSet {
if filter.PropagateMissing {
for i := range fs.ObjectSet {
for _, p := range fs.ObjectSet[i].MissingSet {
// Same behavior as mo.ObjectContentToType()
return soap.WrapVimFault(p.Fault.Fault)
}
}
}
if f(fs.ObjectSet) {
return nil
}

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Copyright (c) 2015-2020 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,110 +17,24 @@ limitations under the License.
package session
import (
"context"
"sync"
"time"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/session/keepalive"
"github.com/vmware/govmomi/vim25/soap"
)
type keepAlive struct {
sync.Mutex
roundTripper soap.RoundTripper
idleTime time.Duration
notifyRequest chan struct{}
notifyStop chan struct{}
notifyWaitGroup sync.WaitGroup
// keepAlive executes a request in the background with the purpose of
// keeping the session active. The response for this request is discarded.
keepAlive func(soap.RoundTripper) error
}
func defaultKeepAlive(roundTripper soap.RoundTripper) error {
_, _ = methods.GetCurrentTime(context.Background(), roundTripper)
return nil
}
// KeepAlive wraps the specified soap.RoundTripper and executes a meaningless
// API request in the background after the RoundTripper has been idle for the
// specified amount of idle time. The keep alive process only starts once a
// user logs in and runs until the user logs out again.
// KeepAlive is a backward compatible wrapper around KeepAliveHandler.
func KeepAlive(roundTripper soap.RoundTripper, idleTime time.Duration) soap.RoundTripper {
return KeepAliveHandler(roundTripper, idleTime, defaultKeepAlive)
return KeepAliveHandler(roundTripper, idleTime, nil)
}
// KeepAliveHandler works as KeepAlive() does, but the handler param can decide how to handle errors.
// For example, if connectivity to ESX/VC is down long enough for a session to expire, a handler can choose to
// Login() on a types.NotAuthenticated error. If handler returns non-nil, the keep alive go routine will be stopped.
// KeepAliveHandler is a backward compatible wrapper around keepalive.NewHandlerSOAP.
func KeepAliveHandler(roundTripper soap.RoundTripper, idleTime time.Duration, handler func(soap.RoundTripper) error) soap.RoundTripper {
k := &keepAlive{
roundTripper: roundTripper,
idleTime: idleTime,
notifyRequest: make(chan struct{}),
}
k.keepAlive = handler
return k
}
func (k *keepAlive) start() {
k.Lock()
defer k.Unlock()
if k.notifyStop != nil {
return
}
// This channel must be closed to terminate idle timer.
k.notifyStop = make(chan struct{})
k.notifyWaitGroup.Add(1)
go func() {
defer k.notifyWaitGroup.Done()
for t := time.NewTimer(k.idleTime); ; {
select {
case <-k.notifyStop:
return
case <-k.notifyRequest:
t.Reset(k.idleTime)
case <-t.C:
if err := k.keepAlive(k.roundTripper); err != nil {
k.stop()
}
t = time.NewTimer(k.idleTime)
}
var f func() error
if handler != nil {
f = func() error {
return handler(roundTripper)
}
}()
}
func (k *keepAlive) stop() {
k.Lock()
defer k.Unlock()
if k.notifyStop != nil {
close(k.notifyStop)
k.notifyWaitGroup.Wait()
k.notifyStop = nil
}
}
func (k *keepAlive) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
err := k.roundTripper.RoundTrip(ctx, req, res)
if err != nil {
return err
}
// Start ticker on login, stop ticker on logout.
switch req.(type) {
case *methods.LoginBody, *methods.LoginExtensionByCertificateBody, *methods.LoginByTokenBody:
k.start()
case *methods.LogoutBody:
k.stop()
}
return nil
return keepalive.NewHandlerSOAP(roundTripper, idleTime, f)
}

View File

@@ -0,0 +1,204 @@
/*
Copyright (c) 2020 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 keepalive
import (
"context"
"errors"
"net/http"
"sync"
"time"
"github.com/vmware/govmomi/vapi/rest"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/soap"
)
// handler contains the generic keep alive settings and logic
type handler struct {
mu sync.Mutex
notifyStop chan struct{}
notifyWaitGroup sync.WaitGroup
idle time.Duration
send func() error
}
// NewHandlerSOAP returns a soap.RoundTripper for use with a vim25.Client
// The idle time specifies the interval in between send() requests. Defaults to 10 minutes.
// The send func is used to keep a session alive. Defaults to calling vim25 GetCurrentTime().
// The keep alive goroutine starts when a Login method is called and runs until Logout is called or send returns an error.
func NewHandlerSOAP(c soap.RoundTripper, idle time.Duration, send func() error) *HandlerSOAP {
h := &handler{
idle: idle,
send: send,
}
if send == nil {
h.send = func() error {
return h.keepAliveSOAP(c)
}
}
return &HandlerSOAP{h, c}
}
// NewHandlerREST returns an http.RoundTripper for use with a rest.Client
// The idle time specifies the interval in between send() requests. Defaults to 10 minutes.
// The send func is used to keep a session alive. Defaults to calling the rest.Client.Session() method
// The keep alive goroutine starts when a Login method is called and runs until Logout is called or send returns an error.
func NewHandlerREST(c *rest.Client, idle time.Duration, send func() error) *HandlerREST {
h := &handler{
idle: idle,
send: send,
}
if send == nil {
h.send = func() error {
return h.keepAliveREST(c)
}
}
return &HandlerREST{h, c.Transport}
}
func (h *handler) keepAliveSOAP(rt soap.RoundTripper) error {
ctx := context.Background()
_, err := methods.GetCurrentTime(ctx, rt)
return err
}
func (h *handler) keepAliveREST(c *rest.Client) error {
ctx := context.Background()
s, err := c.Session(ctx)
if err != nil {
return err
}
if s != nil {
return nil
}
return errors.New(http.StatusText(http.StatusUnauthorized))
}
// Start explicitly starts the keep alive go routine.
// For use with session cache.Client, as cached sessions may not involve Login/Logout via RoundTripper.
func (h *handler) Start() {
h.mu.Lock()
defer h.mu.Unlock()
if h.notifyStop != nil {
return
}
if h.idle == 0 {
h.idle = time.Minute * 10
}
// This channel must be closed to terminate idle timer.
h.notifyStop = make(chan struct{})
h.notifyWaitGroup.Add(1)
go func() {
for t := time.NewTimer(h.idle); ; {
select {
case <-h.notifyStop:
h.notifyWaitGroup.Done()
t.Stop()
return
case <-t.C:
if err := h.send(); err != nil {
h.notifyWaitGroup.Done()
h.Stop()
return
}
t.Reset(h.idle)
}
}
}()
}
// Stop explicitly stops the keep alive go routine.
// For use with session cache.Client, as cached sessions may not involve Login/Logout via RoundTripper.
func (h *handler) Stop() {
h.mu.Lock()
defer h.mu.Unlock()
if h.notifyStop != nil {
close(h.notifyStop)
h.notifyWaitGroup.Wait()
h.notifyStop = nil
}
}
// HandlerSOAP is a keep alive implementation for use with vim25.Client
type HandlerSOAP struct {
*handler
roundTripper soap.RoundTripper
}
// RoundTrip implements soap.RoundTripper
func (h *HandlerSOAP) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
// Stop ticker on logout.
switch req.(type) {
case *methods.LogoutBody:
h.Stop()
}
err := h.roundTripper.RoundTrip(ctx, req, res)
if err != nil {
return err
}
// Start ticker on login.
switch req.(type) {
case *methods.LoginBody, *methods.LoginExtensionByCertificateBody, *methods.LoginByTokenBody:
h.Start()
}
return nil
}
// HandlerREST is a keep alive implementation for use with rest.Client
type HandlerREST struct {
*handler
roundTripper http.RoundTripper
}
// RoundTrip implements http.RoundTripper
func (h *HandlerREST) RoundTrip(req *http.Request) (*http.Response, error) {
if req.URL.Path != "/rest/com/vmware/cis/session" {
return h.roundTripper.RoundTrip(req)
}
if req.Method == http.MethodDelete { // Logout
h.Stop()
}
res, err := h.roundTripper.RoundTrip(req)
if err != nil {
return res, err
}
if req.Method == http.MethodPost { // Login
h.Start()
}
return res, err
}

View File

@@ -18,9 +18,10 @@ package session
import (
"context"
"net/http"
"io/ioutil"
"net/url"
"os"
"strings"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vim25"
@@ -41,6 +42,21 @@ func init() {
}
}
// Secret returns the contents if a file path value is given, otherwise returns value itself.
func Secret(value string) (string, error) {
if len(value) == 0 {
return value, nil
}
contents, err := ioutil.ReadFile(value)
if err != nil {
if os.IsPermission(err) {
return "", err
}
return value, nil
}
return strings.TrimSpace(string(contents)), nil
}
type Manager struct {
client *vim25.Client
userSession *types.UserSession
@@ -107,7 +123,7 @@ func (sm *Manager) LoginExtensionByCertificate(ctx context.Context, key string)
// "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
c.DefaultTransport().TLSClientConfig.InsecureSkipVerify = true
}
req := types.LoginExtensionByCertificate{
@@ -265,3 +281,14 @@ func (sm *Manager) CloneSession(ctx context.Context, ticket string) error {
sm.userSession = &res.Returnval
return nil
}
func (sm *Manager) UpdateServiceMessage(ctx context.Context, message string) error {
req := types.UpdateServiceMessage{
This: sm.Reference(),
Message: message,
}
_, err := methods.UpdateServiceMessage(ctx, sm.client, &req)
return err
}

View File

@@ -36,11 +36,12 @@ type AuthorizationManager struct {
nextID int32
}
func NewAuthorizationManager(ref types.ManagedObjectReference) object.Reference {
m := &AuthorizationManager{}
m.Self = ref
m.RoleList = make([]types.AuthorizationRole, len(esx.RoleList))
copy(m.RoleList, esx.RoleList)
func (m *AuthorizationManager) init(r *Registry) {
if len(m.RoleList) == 0 {
m.RoleList = make([]types.AuthorizationRole, len(esx.RoleList))
copy(m.RoleList, esx.RoleList)
}
m.permissions = make(map[types.ManagedObjectReference][]types.Permission)
l := object.AuthorizationRoleList(m.RoleList)
@@ -52,7 +53,7 @@ func NewAuthorizationManager(ref types.ManagedObjectReference) object.Reference
m.privileges[id] = struct{}{}
}
root := Map.content().RootFolder
root := r.content().RootFolder
for _, u := range DefaultUserGroup {
m.permissions[root] = append(m.permissions[root], types.Permission{
@@ -63,8 +64,6 @@ func NewAuthorizationManager(ref types.ManagedObjectReference) object.Reference
Propagate: true,
})
}
return m
}
func (m *AuthorizationManager) RetrieveEntityPermissions(req *types.RetrieveEntityPermissions) soap.HasFault {
@@ -149,6 +148,85 @@ func (m *AuthorizationManager) RetrieveRolePermissions(req *types.RetrieveRolePe
}
}
func (m *AuthorizationManager) HasPrivilegeOnEntities(req *types.HasPrivilegeOnEntities) soap.HasFault {
var p []types.EntityPrivilege
for _, e := range req.Entity {
priv := types.EntityPrivilege{Entity: e}
for _, id := range req.PrivId {
priv.PrivAvailability = append(priv.PrivAvailability, types.PrivilegeAvailability{
PrivId: id,
IsGranted: true,
})
}
p = append(p, priv)
}
return &methods.HasPrivilegeOnEntitiesBody{
Res: &types.HasPrivilegeOnEntitiesResponse{
Returnval: p,
},
}
}
func (m *AuthorizationManager) HasPrivilegeOnEntity(req *types.HasPrivilegeOnEntity) soap.HasFault {
p := make([]bool, len(req.PrivId))
for i := range req.PrivId {
p[i] = true
}
return &methods.HasPrivilegeOnEntityBody{
Res: &types.HasPrivilegeOnEntityResponse{
Returnval: p,
},
}
}
func (m *AuthorizationManager) HasUserPrivilegeOnEntities(req *types.HasUserPrivilegeOnEntities) soap.HasFault {
var p []types.EntityPrivilege
for _, e := range req.Entities {
priv := types.EntityPrivilege{Entity: e}
for _, id := range req.PrivId {
priv.PrivAvailability = append(priv.PrivAvailability, types.PrivilegeAvailability{
PrivId: id,
IsGranted: true,
})
}
p = append(p, priv)
}
return &methods.HasUserPrivilegeOnEntitiesBody{
Res: &types.HasUserPrivilegeOnEntitiesResponse{
Returnval: p,
},
}
}
func (m *AuthorizationManager) FetchUserPrivilegeOnEntities(req *types.FetchUserPrivilegeOnEntities) soap.HasFault {
admin := object.AuthorizationRoleList(m.RoleList).ByName("Admin").Privilege
var p []types.UserPrivilegeResult
for _, e := range req.Entities {
p = append(p, types.UserPrivilegeResult{
Entity: e,
Privileges: admin,
})
}
return &methods.FetchUserPrivilegeOnEntitiesBody{
Res: &types.FetchUserPrivilegeOnEntitiesResponse{
Returnval: p,
},
}
}
func (m *AuthorizationManager) AddAuthorizationRole(req *types.AddAuthorizationRole) soap.HasFault {
body := &methods.AddAuthorizationRoleBody{}

View File

@@ -17,9 +17,13 @@ limitations under the License.
package simulator
import (
"log"
"math/rand"
"sync/atomic"
"time"
"github.com/google/uuid"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
@@ -33,8 +37,8 @@ type ClusterComputeResource struct {
ruleKey int32
}
func (c *ClusterComputeResource) RenameTask(req *types.Rename_Task) soap.HasFault {
return RenameTask(c, req)
func (c *ClusterComputeResource) RenameTask(ctx *Context, req *types.Rename_Task) soap.HasFault {
return RenameTask(ctx, c, req)
}
type addHost struct {
@@ -50,17 +54,21 @@ func (add *addHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
return nil, &types.NoHost{}
}
host := NewHostSystem(esx.HostSystem)
host.Summary.Config.Name = spec.HostName
host.Name = host.Summary.Config.Name
if add.req.AsConnected {
host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
cr := add.ClusterComputeResource
template := esx.HostSystem
if h := task.ctx.Map.FindByName(spec.UserName, cr.Host); h != nil {
// "clone" an existing host from the inventory
template = h.(*HostSystem).HostSystem
template.Vm = nil
} else {
host.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected
template.Network = cr.Network[:1] // VM Network
}
cr := add.ClusterComputeResource
Map.PutEntity(cr, Map.NewEntity(host))
host := NewHostSystem(template)
host.configure(spec, add.req.AsConnected)
task.ctx.Map.PutEntity(cr, task.ctx.Map.NewEntity(host))
host.Summary.Host = &host.Self
cr.Host = append(cr.Host, host.Reference())
@@ -69,10 +77,10 @@ func (add *addHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
return host.Reference(), nil
}
func (c *ClusterComputeResource) AddHostTask(add *types.AddHost_Task) soap.HasFault {
func (c *ClusterComputeResource) AddHostTask(ctx *Context, add *types.AddHost_Task) soap.HasFault {
return &methods.AddHost_TaskBody{
Res: &types.AddHost_TaskResponse{
Returnval: NewTask(&addHost{c, add}).Run(),
Returnval: NewTask(&addHost{c, add}).Run(ctx),
},
}
}
@@ -265,7 +273,53 @@ func (c *ClusterComputeResource) updateOverridesDRS(cfg *types.ClusterConfigInfo
return nil
}
func (c *ClusterComputeResource) ReconfigureComputeResourceTask(req *types.ReconfigureComputeResource_Task) soap.HasFault {
func (c *ClusterComputeResource) updateOverridesVmOrchestration(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.VmOrchestrationSpec {
var i int
var key types.ManagedObjectReference
exists := false
if spec.Operation == types.ArrayUpdateOperationRemove {
key = spec.RemoveKey.(types.ManagedObjectReference)
} else {
key = spec.Info.Vm
}
for i = range cfg.VmOrchestration {
if cfg.VmOrchestration[i].Vm == key {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
cfg.VmOrchestration = append(cfg.VmOrchestration, *spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
if spec.Info.VmReadiness.ReadyCondition != "" {
cfg.VmOrchestration[i].VmReadiness.ReadyCondition = spec.Info.VmReadiness.ReadyCondition
}
if spec.Info.VmReadiness.PostReadyDelay != 0 {
cfg.VmOrchestration[i].VmReadiness.PostReadyDelay = spec.Info.VmReadiness.PostReadyDelay
}
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.VmOrchestration = append(cfg.VmOrchestration[:i], cfg.VmOrchestration[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) ReconfigureComputeResourceTask(ctx *Context, req *types.ReconfigureComputeResource_Task) soap.HasFault {
task := CreateTask(c, "reconfigureCluster", func(*Task) (types.AnyType, types.BaseMethodFault) {
spec, ok := req.Spec.(*types.ClusterConfigSpecEx)
if !ok {
@@ -277,6 +331,7 @@ func (c *ClusterComputeResource) ReconfigureComputeResourceTask(req *types.Recon
c.updateGroups,
c.updateOverridesDAS,
c.updateOverridesDRS,
c.updateOverridesVmOrchestration,
}
for _, update := range updates {
@@ -290,13 +345,69 @@ func (c *ClusterComputeResource) ReconfigureComputeResourceTask(req *types.Recon
return &methods.ReconfigureComputeResource_TaskBody{
Res: &types.ReconfigureComputeResource_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}
func CreateClusterComputeResource(f *Folder, name string, spec types.ClusterConfigSpecEx) (*ClusterComputeResource, types.BaseMethodFault) {
if e := Map.FindByName(name, f.ChildEntity); e != nil {
func (c *ClusterComputeResource) PlaceVm(ctx *Context, req *types.PlaceVm) soap.HasFault {
body := new(methods.PlaceVmBody)
if len(c.Host) == 0 {
body.Fault_ = Fault("", new(types.InvalidState))
return body
}
res := types.ClusterRecommendation{
Key: "1",
Type: "V1",
Time: time.Now(),
Rating: 1,
Reason: string(types.RecommendationReasonCodeXvmotionPlacement),
ReasonText: string(types.RecommendationReasonCodeXvmotionPlacement),
Target: &c.Self,
}
hosts := req.PlacementSpec.Hosts
if len(hosts) == 0 {
hosts = c.Host
}
datastores := req.PlacementSpec.Datastores
if len(datastores) == 0 {
datastores = c.Datastore
}
spec := &types.VirtualMachineRelocateSpec{
Datastore: &datastores[rand.Intn(len(c.Datastore))],
Host: &hosts[rand.Intn(len(c.Host))],
Pool: c.ResourcePool,
}
switch types.PlacementSpecPlacementType(req.PlacementSpec.PlacementType) {
case types.PlacementSpecPlacementTypeClone, types.PlacementSpecPlacementTypeCreate:
res.Action = append(res.Action, &types.PlacementAction{
Vm: req.PlacementSpec.Vm,
TargetHost: spec.Host,
RelocateSpec: spec,
})
default:
log.Printf("unsupported placement type: %s", req.PlacementSpec.PlacementType)
body.Fault_ = Fault("", new(types.NotSupported))
return body
}
body.Res = &types.PlaceVmResponse{
Returnval: types.PlacementResult{
Recommendations: []types.ClusterRecommendation{res},
},
}
return body
}
func CreateClusterComputeResource(ctx *Context, f *Folder, name string, spec types.ClusterConfigSpecEx) (*ClusterComputeResource, types.BaseMethodFault) {
if e := ctx.Map.FindByName(name, f.ChildEntity); e != nil {
return nil, &types.DuplicateName{
Name: e.Entity().Name,
Object: e.Reference(),
@@ -306,6 +417,7 @@ func CreateClusterComputeResource(f *Folder, name string, spec types.ClusterConf
cluster := &ClusterComputeResource{}
cluster.EnvironmentBrowser = newEnvironmentBrowser()
cluster.Name = name
cluster.Network = ctx.Map.getEntityDatacenter(f).defaultNetwork()
cluster.Summary = &types.ClusterComputeResourceSummary{
UsageSummary: new(types.ClusterUsageSummary),
}
@@ -317,10 +429,10 @@ func CreateClusterComputeResource(f *Folder, name string, spec types.ClusterConf
config.DrsConfig.Enabled = types.NewBool(true)
pool := NewResourcePool()
Map.PutEntity(cluster, Map.NewEntity(pool))
ctx.Map.PutEntity(cluster, ctx.Map.NewEntity(pool))
cluster.ResourcePool = &pool.Self
f.putChild(cluster)
folderPutChild(ctx, &f.Folder, cluster)
pool.Owner = cluster.Self
return cluster, nil

View File

@@ -17,18 +17,49 @@ limitations under the License.
package simulator
import (
"archive/tar"
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"os/exec"
"path"
"regexp"
"strconv"
"strings"
"time"
"github.com/google/uuid"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
)
var (
shell = "/bin/sh"
)
func init() {
if sh, err := exec.LookPath("bash"); err != nil {
shell = sh
}
}
// container provides methods to manage a container within a simulator VM lifecycle.
type container struct {
id string
id string
name string
}
type networkSettings struct {
Gateway string
IPAddress string
IPPrefixLen int
MacAddress string
}
// inspect applies container network settings to vm.Guest properties.
@@ -38,11 +69,13 @@ func (c *container) inspect(vm *VirtualMachine) error {
}
var objects []struct {
State struct {
Running bool
Paused bool
}
NetworkSettings struct {
Gateway string
IPAddress string
IPPrefixLen int
MacAddress string
networkSettings
Networks map[string]networkSettings
}
}
@@ -59,9 +92,19 @@ func (c *container) inspect(vm *VirtualMachine) error {
vm.logPrintf("%s: %s", vm.Config.Annotation, string(out))
for _, o := range objects {
s := o.NetworkSettings
if s.IPAddress == "" {
continue
s := o.NetworkSettings.networkSettings
for _, n := range o.NetworkSettings.Networks {
s = n
break
}
if o.State.Paused {
vm.Runtime.PowerState = types.VirtualMachinePowerStateSuspended
} else if o.State.Running {
vm.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOn
} else {
vm.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff
}
vm.Guest.IpAddress = s.IPAddress
@@ -77,8 +120,104 @@ func (c *container) inspect(vm *VirtualMachine) error {
return nil
}
func (c *container) prepareGuestOperation(
vm *VirtualMachine,
auth types.BaseGuestAuthentication) types.BaseMethodFault {
if c.id == "" {
return new(types.GuestOperationsUnavailable)
}
if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOn {
return &types.InvalidPowerState{
RequestedState: types.VirtualMachinePowerStatePoweredOn,
ExistingState: vm.Runtime.PowerState,
}
}
switch creds := auth.(type) {
case *types.NamePasswordAuthentication:
if creds.Username == "" || creds.Password == "" {
return new(types.InvalidGuestLogin)
}
default:
return new(types.InvalidGuestLogin)
}
return nil
}
var sanitizeNameRx = regexp.MustCompile(`[\(\)\s]`)
func sanitizeName(name string) string {
return sanitizeNameRx.ReplaceAllString(name, "-")
}
// createDMI writes BIOS UUID DMI files to a container volume
func (c *container) createDMI(vm *VirtualMachine, name string) error {
image := os.Getenv("VCSIM_BUSYBOX")
if image == "" {
image = "busybox"
}
cmd := exec.Command("docker", "run", "--rm", "-i", "-v", name+":"+"/"+name, image, "tar", "-C", "/"+name, "-xf", "-")
stdin, err := cmd.StdinPipe()
if err != nil {
return err
}
err = cmd.Start()
if err != nil {
return err
}
tw := tar.NewWriter(stdin)
dmi := []struct {
name string
val func(uuid.UUID) string
}{
{"product_uuid", productUUID},
{"product_serial", productSerial},
}
for _, file := range dmi {
val := file.val(vm.uid)
_ = tw.WriteHeader(&tar.Header{
Name: file.name,
Size: int64(len(val) + 1),
Mode: 0444,
ModTime: time.Now(),
})
_, _ = fmt.Fprintln(tw, val)
}
_ = tw.Close()
_ = stdin.Close()
if err := cmd.Wait(); err != nil {
stderr := ""
if xerr, ok := err.(*exec.ExitError); ok {
stderr = string(xerr.Stderr)
}
log.Printf("%s %s: %s %s", vm.Name, cmd.Args, err, stderr)
return err
}
return nil
}
var (
toolsRunning = []types.PropertyChange{
{Name: "guest.toolsStatus", Val: types.VirtualMachineToolsStatusToolsOk},
{Name: "guest.toolsRunningStatus", Val: string(types.VirtualMachineToolsRunningStatusGuestToolsRunning)},
}
toolsNotRunning = []types.PropertyChange{
{Name: "guest.toolsStatus", Val: types.VirtualMachineToolsStatusToolsNotRunning},
{Name: "guest.toolsRunningStatus", Val: string(types.VirtualMachineToolsRunningStatusGuestToolsNotRunning)},
}
)
// start runs the container if specified by the RUN.container extraConfig property.
func (c *container) start(vm *VirtualMachine) {
func (c *container) start(ctx *Context, vm *VirtualMachine) {
if c.id != "" {
start := "start"
if vm.Runtime.PowerState == types.VirtualMachinePowerStateSuspended {
@@ -88,11 +227,14 @@ func (c *container) start(vm *VirtualMachine) {
err := cmd.Run()
if err != nil {
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
} else {
ctx.Map.Update(vm, toolsRunning)
}
return
}
var args []string
var env []string
for _, opt := range vm.Config.ExtraConfig {
val := opt.GetOptionValue()
@@ -103,32 +245,124 @@ func (c *container) start(vm *VirtualMachine) {
args = []string{run}
}
break
continue
}
if strings.HasPrefix(val.Key, "guestinfo.") {
key := strings.Replace(strings.ToUpper(val.Key), ".", "_", -1)
env = append(env, "--env", fmt.Sprintf("VMX_%s=%s", key, val.Value.(string)))
}
}
if len(args) == 0 {
return
}
if len(env) != 0 {
// Configure env as the data access method for cloud-init-vmware-guestinfo
env = append(env, "--env", "VMX_GUESTINFO=true")
}
args = append([]string{"run", "-d", "--name", vm.Name}, args...)
cmd := exec.Command("docker", args...)
c.name = fmt.Sprintf("vcsim-%s-%s", sanitizeName(vm.Name), vm.uid)
run := append([]string{"docker", "run", "-d", "--name", c.name}, env...)
if err := c.createDMI(vm, c.name); err != nil {
return
}
run = append(run, "-v", fmt.Sprintf("%s:%s:ro", c.name, "/sys/class/dmi/id"))
args = append(run, args...)
cmd := exec.Command(shell, "-c", strings.Join(args, " "))
out, err := cmd.Output()
if err != nil {
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
stderr := ""
if xerr, ok := err.(*exec.ExitError); ok {
stderr = string(xerr.Stderr)
}
log.Printf("%s %s: %s %s", vm.Name, cmd.Args, err, stderr)
return
}
ctx.Map.Update(vm, toolsRunning)
c.id = strings.TrimSpace(string(out))
vm.logPrintf("%s %s: %s", cmd.Path, cmd.Args, c.id)
if err = c.inspect(vm); err != nil {
log.Printf("%s inspect %s: %s", vm.Name, c.id, err)
}
// Start watching the container resource.
go c.watchContainer(vm)
}
// watchContainer monitors the underlying container and updates the VM
// properties based on the container status. This occurs until either
// the container or the VM is removed.
func (c *container) watchContainer(vm *VirtualMachine) {
inspectInterval := time.Duration(5 * time.Second)
if d, err := time.ParseDuration(os.Getenv("VCSIM_INSPECT_INTERVAL")); err == nil {
inspectInterval = d
}
var (
ctx = SpoofContext()
done = make(chan struct{})
ticker = time.NewTicker(inspectInterval)
)
stopUpdatingVmFromContainer := func() {
ticker.Stop()
close(done)
}
destroyVm := func() {
// If the container cannot be found then destroy this VM.
taskRef := vm.DestroyTask(ctx, &types.Destroy_Task{
This: vm.Self,
}).(*methods.Destroy_TaskBody).Res.Returnval
task := ctx.Map.Get(taskRef).(*Task)
// Wait for the task to complete and see if there is an error.
task.Wait()
if task.Info.Error != nil {
vm.logPrintf("failed to destroy vm: err=%v", *task.Info.Error)
}
}
updateVmFromContainer := func() {
// Exit the monitor loop if the VM was removed from the API side.
if c.id == "" {
stopUpdatingVmFromContainer()
return
}
if err := c.inspect(vm); err != nil {
// If there is an error inspecting the container because it no
// longer exists, then destroy the VM as well. Please note the
// reason this logic does not invoke stopUpdatingVmFromContainer
// is because that will be handled the next time this function
// is entered and c.id is empty.
if err, ok := err.(*exec.ExitError); ok {
if strings.Contains(string(err.Stderr), "No such object") {
destroyVm()
}
}
}
}
// Update the VM from the container at regular intervals until the done
// channel is closed.
for {
select {
case <-ticker.C:
ctx.WithLock(vm, updateVmFromContainer)
case <-done:
return
}
}
}
// stop the container (if any) for the given vm.
func (c *container) stop(vm *VirtualMachine) {
func (c *container) stop(ctx *Context, vm *VirtualMachine) {
if c.id == "" {
return
}
@@ -137,11 +371,13 @@ func (c *container) stop(vm *VirtualMachine) {
err := cmd.Run()
if err != nil {
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
} else {
ctx.Map.Update(vm, toolsNotRunning)
}
}
// pause the container (if any) for the given vm.
func (c *container) pause(vm *VirtualMachine) {
func (c *container) pause(ctx *Context, vm *VirtualMachine) {
if c.id == "" {
return
}
@@ -150,6 +386,23 @@ func (c *container) pause(vm *VirtualMachine) {
err := cmd.Run()
if err != nil {
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
} else {
ctx.Map.Update(vm, toolsNotRunning)
}
}
// restart the container (if any) for the given vm.
func (c *container) restart(ctx *Context, vm *VirtualMachine) {
if c.id == "" {
return
}
cmd := exec.Command("docker", "restart", c.id)
err := cmd.Run()
if err != nil {
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
} else {
ctx.Map.Update(vm, toolsRunning)
}
}
@@ -159,9 +412,160 @@ func (c *container) remove(vm *VirtualMachine) {
return
}
cmd := exec.Command("docker", "rm", "-f", c.id)
err := cmd.Run()
args := [][]string{
{"rm", "-v", "-f", c.id},
{"volume", "rm", "-f", c.name},
}
for i := range args {
cmd := exec.Command("docker", args[i]...)
err := cmd.Run()
if err != nil {
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
}
}
c.id = ""
}
func (c *container) exec(ctx *Context, vm *VirtualMachine, auth types.BaseGuestAuthentication, args []string) (string, types.BaseMethodFault) {
fault := vm.run.prepareGuestOperation(vm, auth)
if fault != nil {
return "", fault
}
args = append([]string{"exec", vm.run.id}, args...)
cmd := exec.Command("docker", args...)
res, err := cmd.CombinedOutput()
if err != nil {
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
log.Printf("%s: %s (%s)", vm.Self, cmd.Args, string(res))
return "", new(types.GuestOperationsFault)
}
return strings.TrimSpace(string(res)), nil
}
// From https://docs.docker.com/engine/reference/commandline/cp/ :
// > It is not possible to copy certain system files such as resources under /proc, /sys, /dev, tmpfs, and mounts created by the user in the container.
// > However, you can still copy such files by manually running tar in docker exec.
func guestUpload(id string, file string, r *http.Request) error {
cmd := exec.Command("docker", "exec", "-i", id, "tar", "Cxf", path.Dir(file), "-")
cmd.Stderr = os.Stderr
stdin, err := cmd.StdinPipe()
if err != nil {
return err
}
if err = cmd.Start(); err != nil {
return err
}
tw := tar.NewWriter(stdin)
_ = tw.WriteHeader(&tar.Header{
Name: path.Base(file),
Size: r.ContentLength,
Mode: 0444,
ModTime: time.Now(),
})
_, _ = io.Copy(tw, r.Body)
_ = tw.Close()
_ = stdin.Close()
_ = r.Body.Close()
return cmd.Wait()
}
func guestDownload(id string, file string, w http.ResponseWriter) error {
cmd := exec.Command("docker", "exec", id, "tar", "Ccf", path.Dir(file), "-", path.Base(file))
cmd.Stderr = os.Stderr
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
if err = cmd.Start(); err != nil {
return err
}
tr := tar.NewReader(stdout)
header, err := tr.Next()
if err != nil {
return err
}
w.Header().Set("Content-Length", strconv.FormatInt(header.Size, 10))
_, _ = io.Copy(w, tr)
return cmd.Wait()
}
const guestPrefix = "/guestFile/"
// ServeGuest handles container guest file upload/download
func ServeGuest(w http.ResponseWriter, r *http.Request) {
// Real vCenter form: /guestFile?id=139&token=...
// vcsim form: /guestFile/tmp/foo/bar?id=ebc8837b8cb6&token=...
id := r.URL.Query().Get("id")
file := strings.TrimPrefix(r.URL.Path, guestPrefix[:len(guestPrefix)-1])
var err error
switch r.Method {
case http.MethodPut:
err = guestUpload(id, file, r)
case http.MethodGet:
err = guestDownload(id, file, w)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
if err != nil {
log.Printf("%s %s: %s", r.Method, r.URL, err)
w.WriteHeader(http.StatusInternalServerError)
}
}
// productSerial returns the uuid in /sys/class/dmi/id/product_serial format
func productSerial(id uuid.UUID) string {
var dst [len(id)*2 + len(id) - 1]byte
j := 0
for i := 0; i < len(id); i++ {
hex.Encode(dst[j:j+2], id[i:i+1])
j += 3
if j < len(dst) {
s := j - 1
if s == len(dst)/2 {
dst[s] = '-'
} else {
dst[s] = ' '
}
}
}
return fmt.Sprintf("VMware-%s", string(dst[:]))
}
// productUUID returns the uuid in /sys/class/dmi/id/product_uuid format
func productUUID(id uuid.UUID) string {
var dst [36]byte
hex.Encode(dst[0:2], id[3:4])
hex.Encode(dst[2:4], id[2:3])
hex.Encode(dst[4:6], id[1:2])
hex.Encode(dst[6:8], id[0:1])
dst[8] = '-'
hex.Encode(dst[9:11], id[5:6])
hex.Encode(dst[11:13], id[4:5])
dst[13] = '-'
hex.Encode(dst[14:16], id[7:8])
hex.Encode(dst[16:18], id[6:7])
dst[18] = '-'
hex.Encode(dst[19:23], id[8:10])
dst[23] = '-'
hex.Encode(dst[24:], id[10:])
return strings.ToUpper(string(dst[:]))
}

View File

@@ -17,7 +17,6 @@ limitations under the License.
package simulator
import (
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
@@ -30,19 +29,13 @@ type CustomFieldsManager struct {
nextKey int32
}
func NewCustomFieldsManager(ref types.ManagedObjectReference) object.Reference {
m := &CustomFieldsManager{}
m.Self = ref
return m
}
// Iterates through all entities of passed field type;
// Removes found field from their custom field properties.
func entitiesFieldRemove(field types.CustomFieldDef) {
entities := Map.All(field.ManagedObjectType)
func entitiesFieldRemove(ctx *Context, field types.CustomFieldDef) {
entities := ctx.Map.All(field.ManagedObjectType)
for _, e := range entities {
entity := e.Entity()
Map.WithLock(entity, func() {
ctx.WithLock(entity, func() {
aFields := entity.AvailableField
for i, aField := range aFields {
if aField.Key == field.Key {
@@ -72,11 +65,11 @@ func entitiesFieldRemove(field types.CustomFieldDef) {
// Iterates through all entities of passed field type;
// Renames found field in entity's AvailableField property.
func entitiesFieldRename(field types.CustomFieldDef) {
entities := Map.All(field.ManagedObjectType)
func entitiesFieldRename(ctx *Context, field types.CustomFieldDef) {
entities := ctx.Map.All(field.ManagedObjectType)
for _, e := range entities {
entity := e.Entity()
Map.WithLock(entity, func() {
ctx.WithLock(entity, func() {
aFields := entity.AvailableField
for i, aField := range aFields {
if aField.Key == field.Key {
@@ -109,7 +102,7 @@ func (c *CustomFieldsManager) findByKey(key int32) (int, *types.CustomFieldDef)
return -1, nil
}
func (c *CustomFieldsManager) AddCustomFieldDef(req *types.AddCustomFieldDef) soap.HasFault {
func (c *CustomFieldsManager) AddCustomFieldDef(ctx *Context, req *types.AddCustomFieldDef) soap.HasFault {
body := &methods.AddCustomFieldDefBody{}
_, field := c.findByNameType(req.Name, req.MoType)
@@ -130,10 +123,10 @@ func (c *CustomFieldsManager) AddCustomFieldDef(req *types.AddCustomFieldDef) so
FieldInstancePrivileges: req.FieldPolicy,
}
entities := Map.All(req.MoType)
entities := ctx.Map.All(req.MoType)
for _, e := range entities {
entity := e.Entity()
Map.WithLock(entity, func() {
ctx.WithLock(entity, func() {
entity.AvailableField = append(entity.AvailableField, def)
})
}
@@ -147,7 +140,7 @@ func (c *CustomFieldsManager) AddCustomFieldDef(req *types.AddCustomFieldDef) so
return body
}
func (c *CustomFieldsManager) RemoveCustomFieldDef(req *types.RemoveCustomFieldDef) soap.HasFault {
func (c *CustomFieldsManager) RemoveCustomFieldDef(ctx *Context, req *types.RemoveCustomFieldDef) soap.HasFault {
body := &methods.RemoveCustomFieldDefBody{}
i, field := c.findByKey(req.Key)
@@ -156,7 +149,7 @@ func (c *CustomFieldsManager) RemoveCustomFieldDef(req *types.RemoveCustomFieldD
return body
}
entitiesFieldRemove(*field)
entitiesFieldRemove(ctx, *field)
c.Field = append(c.Field[:i], c.Field[i+1:]...)
@@ -164,7 +157,7 @@ func (c *CustomFieldsManager) RemoveCustomFieldDef(req *types.RemoveCustomFieldD
return body
}
func (c *CustomFieldsManager) RenameCustomFieldDef(req *types.RenameCustomFieldDef) soap.HasFault {
func (c *CustomFieldsManager) RenameCustomFieldDef(ctx *Context, req *types.RenameCustomFieldDef) soap.HasFault {
body := &methods.RenameCustomFieldDefBody{}
_, field := c.findByKey(req.Key)
@@ -175,7 +168,7 @@ func (c *CustomFieldsManager) RenameCustomFieldDef(req *types.RenameCustomFieldD
field.Name = req.Name
entitiesFieldRename(*field)
entitiesFieldRename(ctx, *field)
body.Res = &types.RenameCustomFieldDefResponse{}
return body
@@ -195,8 +188,30 @@ func (c *CustomFieldsManager) SetField(ctx *Context, req *types.SetField) soap.H
Value: req.Value,
}
entity := Map.Get(req.Entity).(mo.Entity).Entity()
removeIndex := func(s []types.BaseCustomFieldValue, i int) []types.BaseCustomFieldValue {
new := make([]types.BaseCustomFieldValue, 0)
new = append(new, s[:i]...)
return append(new, s[i+1:]...)
}
removeExistingValues := func(s []types.BaseCustomFieldValue) []types.BaseCustomFieldValue {
for i := 0; i < len(s); {
if s[i].GetCustomFieldValue().Key == newValue.GetCustomFieldValue().Key {
s = removeIndex(s, i)
}
i++
}
return s
}
entity := ctx.Map.Get(req.Entity).(mo.Entity).Entity()
ctx.WithLock(entity, func() {
// Check if custom value and value are already set. If so, remove them.
entity.CustomValue = removeExistingValues(entity.CustomValue)
entity.Value = removeExistingValues(entity.Value)
// Add the new value
entity.CustomValue = append(entity.CustomValue, newValue)
entity.Value = append(entity.Value, newValue)
})

View File

@@ -0,0 +1,349 @@
/*
Copyright (c) 2019 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"
"sync/atomic"
"time"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
var DefaultCustomizationSpec = []types.CustomizationSpecItem{
{
Info: types.CustomizationSpecInfo{
Name: "vcsim-linux",
Description: "",
Type: "Linux",
ChangeVersion: "1569965707",
LastUpdateTime: types.NewTime(time.Now()),
},
Spec: types.CustomizationSpec{
Options: &types.CustomizationLinuxOptions{},
Identity: &types.CustomizationLinuxPrep{
CustomizationIdentitySettings: types.CustomizationIdentitySettings{},
HostName: &types.CustomizationVirtualMachineName{},
Domain: "eng.vmware.com",
TimeZone: "Pacific/Apia",
HwClockUTC: types.NewBool(true),
},
GlobalIPSettings: types.CustomizationGlobalIPSettings{
DnsSuffixList: nil,
DnsServerList: []string{"127.0.1.1"},
},
NicSettingMap: []types.CustomizationAdapterMapping{
{
MacAddress: "",
Adapter: types.CustomizationIPSettings{
Ip: &types.CustomizationDhcpIpGenerator{},
SubnetMask: "",
Gateway: nil,
IpV6Spec: (*types.CustomizationIPSettingsIpV6AddressSpec)(nil),
DnsServerList: nil,
DnsDomain: "",
PrimaryWINS: "",
SecondaryWINS: "",
NetBIOS: "",
},
},
},
EncryptionKey: nil,
},
},
{
Info: types.CustomizationSpecInfo{
Name: "vcsim-linux-static",
Description: "",
Type: "Linux",
ChangeVersion: "1569969598",
LastUpdateTime: types.NewTime(time.Now()),
},
Spec: types.CustomizationSpec{
Options: &types.CustomizationLinuxOptions{},
Identity: &types.CustomizationLinuxPrep{
CustomizationIdentitySettings: types.CustomizationIdentitySettings{},
HostName: &types.CustomizationPrefixName{
CustomizationName: types.CustomizationName{},
Base: "vcsim",
},
Domain: "eng.vmware.com",
TimeZone: "Africa/Cairo",
HwClockUTC: types.NewBool(true),
},
GlobalIPSettings: types.CustomizationGlobalIPSettings{
DnsSuffixList: nil,
DnsServerList: []string{"127.0.1.1"},
},
NicSettingMap: []types.CustomizationAdapterMapping{
{
MacAddress: "",
Adapter: types.CustomizationIPSettings{
Ip: &types.CustomizationUnknownIpGenerator{},
SubnetMask: "255.255.255.0",
Gateway: []string{"10.0.0.1"},
IpV6Spec: (*types.CustomizationIPSettingsIpV6AddressSpec)(nil),
DnsServerList: nil,
DnsDomain: "",
PrimaryWINS: "",
SecondaryWINS: "",
NetBIOS: "",
},
},
},
EncryptionKey: nil,
},
},
{
Info: types.CustomizationSpecInfo{
Name: "vcsim-windows-static",
Description: "",
Type: "Windows",
ChangeVersion: "1569978029",
LastUpdateTime: types.NewTime(time.Now()),
},
Spec: types.CustomizationSpec{
Options: &types.CustomizationWinOptions{
CustomizationOptions: types.CustomizationOptions{},
ChangeSID: true,
DeleteAccounts: false,
Reboot: "",
},
Identity: &types.CustomizationSysprep{
CustomizationIdentitySettings: types.CustomizationIdentitySettings{},
GuiUnattended: types.CustomizationGuiUnattended{
Password: (*types.CustomizationPassword)(nil),
TimeZone: 2,
AutoLogon: false,
AutoLogonCount: 1,
},
UserData: types.CustomizationUserData{
FullName: "vcsim",
OrgName: "VMware",
ComputerName: &types.CustomizationVirtualMachineName{},
ProductId: "",
},
GuiRunOnce: (*types.CustomizationGuiRunOnce)(nil),
Identification: types.CustomizationIdentification{
JoinWorkgroup: "WORKGROUP",
JoinDomain: "",
DomainAdmin: "",
DomainAdminPassword: (*types.CustomizationPassword)(nil),
},
LicenseFilePrintData: &types.CustomizationLicenseFilePrintData{
AutoMode: "perServer",
AutoUsers: 5,
},
},
GlobalIPSettings: types.CustomizationGlobalIPSettings{},
NicSettingMap: []types.CustomizationAdapterMapping{
{
MacAddress: "",
Adapter: types.CustomizationIPSettings{
Ip: &types.CustomizationUnknownIpGenerator{},
SubnetMask: "255.255.255.0",
Gateway: []string{"10.0.0.1"},
IpV6Spec: (*types.CustomizationIPSettingsIpV6AddressSpec)(nil),
DnsServerList: nil,
DnsDomain: "",
PrimaryWINS: "",
SecondaryWINS: "",
NetBIOS: "",
},
},
},
EncryptionKey: []uint8{0x30},
},
},
{
Info: types.CustomizationSpecInfo{
Name: "vcsim-windows-domain",
Description: "",
Type: "Windows",
ChangeVersion: "1569970234",
LastUpdateTime: types.NewTime(time.Now()),
},
Spec: types.CustomizationSpec{
Options: &types.CustomizationWinOptions{
CustomizationOptions: types.CustomizationOptions{},
ChangeSID: true,
DeleteAccounts: false,
Reboot: "",
},
Identity: &types.CustomizationSysprep{
CustomizationIdentitySettings: types.CustomizationIdentitySettings{},
GuiUnattended: types.CustomizationGuiUnattended{
Password: &types.CustomizationPassword{
Value: "3Gs...==",
PlainText: false,
},
TimeZone: 15,
AutoLogon: false,
AutoLogonCount: 1,
},
UserData: types.CustomizationUserData{
FullName: "dougm",
OrgName: "VMware",
ComputerName: &types.CustomizationVirtualMachineName{},
ProductId: "",
},
GuiRunOnce: (*types.CustomizationGuiRunOnce)(nil),
Identification: types.CustomizationIdentification{
JoinWorkgroup: "",
JoinDomain: "DOMAIN",
DomainAdmin: "vcsim",
DomainAdminPassword: &types.CustomizationPassword{
Value: "H3g...==",
PlainText: false,
},
},
LicenseFilePrintData: &types.CustomizationLicenseFilePrintData{
AutoMode: "perServer",
AutoUsers: 5,
},
},
GlobalIPSettings: types.CustomizationGlobalIPSettings{},
NicSettingMap: []types.CustomizationAdapterMapping{
{
MacAddress: "",
Adapter: types.CustomizationIPSettings{
Ip: &types.CustomizationUnknownIpGenerator{},
SubnetMask: "255.255.255.0",
Gateway: []string{"10.0.0.1"},
IpV6Spec: (*types.CustomizationIPSettingsIpV6AddressSpec)(nil),
DnsServerList: nil,
DnsDomain: "",
PrimaryWINS: "",
SecondaryWINS: "",
NetBIOS: "",
},
},
},
EncryptionKey: []uint8{0x30},
},
},
}
type CustomizationSpecManager struct {
mo.CustomizationSpecManager
items []types.CustomizationSpecItem
}
func (m *CustomizationSpecManager) init(r *Registry) {
m.items = DefaultCustomizationSpec
}
var customizeNameCounter uint64
func customizeName(vm *VirtualMachine, base types.BaseCustomizationName) string {
n := atomic.AddUint64(&customizeNameCounter, 1)
switch name := base.(type) {
case *types.CustomizationPrefixName:
return fmt.Sprintf("%s-%d", name.Base, n)
case *types.CustomizationCustomName:
return fmt.Sprintf("%s-%d", name.Argument, n)
case *types.CustomizationFixedName:
return name.Name
case *types.CustomizationUnknownName:
return ""
case *types.CustomizationVirtualMachineName:
return fmt.Sprintf("%s-%d", vm.Name, n)
default:
return ""
}
}
func (m *CustomizationSpecManager) DoesCustomizationSpecExist(ctx *Context, req *types.DoesCustomizationSpecExist) soap.HasFault {
exists := false
for _, item := range m.items {
if item.Info.Name == req.Name {
exists = true
break
}
}
return &methods.DoesCustomizationSpecExistBody{
Res: &types.DoesCustomizationSpecExistResponse{
Returnval: exists,
},
}
}
func (m *CustomizationSpecManager) GetCustomizationSpec(ctx *Context, req *types.GetCustomizationSpec) soap.HasFault {
body := new(methods.GetCustomizationSpecBody)
for _, item := range m.items {
if item.Info.Name == req.Name {
body.Res = &types.GetCustomizationSpecResponse{
Returnval: item,
}
return body
}
}
body.Fault_ = Fault("", new(types.NotFound))
return body
}
func (m *CustomizationSpecManager) CreateCustomizationSpec(ctx *Context, req *types.CreateCustomizationSpec) soap.HasFault {
body := new(methods.CreateCustomizationSpecBody)
for _, item := range m.items {
if item.Info.Name == req.Item.Info.Name {
body.Fault_ = Fault("", &types.AlreadyExists{Name: req.Item.Info.Name})
return body
}
}
m.items = append(m.items, req.Item)
body.Res = new(types.CreateCustomizationSpecResponse)
return body
}
func (m *CustomizationSpecManager) OverwriteCustomizationSpec(ctx *Context, req *types.OverwriteCustomizationSpec) soap.HasFault {
body := new(methods.OverwriteCustomizationSpecBody)
for i, item := range m.items {
if item.Info.Name == req.Item.Info.Name {
m.items[i] = req.Item
body.Res = new(types.OverwriteCustomizationSpecResponse)
return body
}
}
body.Fault_ = Fault("", new(types.NotFound))
return body
}
func (m *CustomizationSpecManager) Get() mo.Reference {
clone := *m
for i := range clone.items {
clone.Info = append(clone.Info, clone.items[i].Info)
}
return &clone
}

View File

@@ -17,6 +17,7 @@ limitations under the License.
package simulator
import (
"log"
"strings"
"github.com/vmware/govmomi/simulator/esx"
@@ -33,7 +34,7 @@ type Datacenter struct {
}
// NewDatacenter creates a Datacenter and its child folders.
func NewDatacenter(f *Folder) *Datacenter {
func NewDatacenter(ctx *Context, f *mo.Folder) *Datacenter {
dc := &Datacenter{
isESX: f.Self == esx.RootFolder.Self,
}
@@ -42,18 +43,22 @@ func NewDatacenter(f *Folder) *Datacenter {
dc.Datacenter = esx.Datacenter
}
f.putChild(dc)
folderPutChild(ctx, f, dc)
dc.createFolders()
dc.createFolders(ctx)
return dc
}
func (dc *Datacenter) RenameTask(ctx *Context, r *types.Rename_Task) soap.HasFault {
return RenameTask(ctx, dc, r)
}
// Create Datacenter Folders.
// Every Datacenter has 4 inventory Folders: Vm, Host, Datastore and Network.
// The ESX folder child types are limited to 1 type.
// The VC folders have additional child types, including nested folders.
func (dc *Datacenter) createFolders() {
func (dc *Datacenter) createFolders(ctx *Context) {
folders := []struct {
ref *types.ManagedObjectReference
name string
@@ -72,10 +77,10 @@ func (dc *Datacenter) createFolders() {
if dc.isESX {
folder.ChildType = f.types[:1]
folder.Self = *f.ref
Map.PutEntity(dc, folder)
ctx.Map.PutEntity(dc, folder)
} else {
folder.ChildType = f.types
e := Map.PutEntity(dc, folder)
e := ctx.Map.PutEntity(dc, folder)
// propagate the generated morefs to Datacenter
ref := e.Reference()
@@ -84,7 +89,7 @@ func (dc *Datacenter) createFolders() {
}
}
net := Map.Get(dc.NetworkFolder).(*Folder)
net := ctx.Map.Get(dc.NetworkFolder).(*Folder)
for _, ref := range esx.Datacenter.Network {
// Add VM Network by default to each Datacenter
@@ -96,10 +101,41 @@ func (dc *Datacenter) createFolders() {
network.Self.Value = "" // we want a different moid per-DC
}
net.putChild(network)
folderPutChild(ctx, &net.Folder, network)
}
}
func (dc *Datacenter) defaultNetwork() []types.ManagedObjectReference {
return dc.Network[:1] // VM Network
}
// folder returns the Datacenter folder that can contain the given object type
func (dc *Datacenter) folder(obj mo.Entity) *mo.Folder {
folders := []types.ManagedObjectReference{
dc.VmFolder,
dc.HostFolder,
dc.DatastoreFolder,
dc.NetworkFolder,
}
otype := getManagedObject(obj).Type()
rtype := obj.Reference().Type
for i := range folders {
folder, _ := asFolderMO(Map.Get(folders[i]))
for _, kind := range folder.ChildType {
if rtype == kind {
return folder
}
if f, ok := otype.FieldByName(kind); ok && f.Anonymous {
return folder
}
}
}
log.Panicf("failed to find folder for type=%s", rtype)
return nil
}
func datacenterEventArgument(obj mo.Entity) *types.DatacenterEventArgument {
dc, ok := obj.(*Datacenter)
if !ok {
@@ -117,24 +153,46 @@ func (dc *Datacenter) PowerOnMultiVMTask(ctx *Context, req *types.PowerOnMultiVM
return nil, new(types.NotImplemented)
}
// Return per-VM tasks, structured as:
// thisTask.result - DC level task
// +- []Attempted
// +- subTask.result - VM level powerOn task result
// +- ...
res := types.ClusterPowerOnVmResult{}
res.Attempted = []types.ClusterAttemptedVmInfo{}
for _, ref := range req.Vm {
vm := Map.Get(ref).(*VirtualMachine)
Map.WithLock(vm, func() {
vm.PowerOnVMTask(ctx, &types.PowerOnVM_Task{})
vm := ctx.Map.Get(ref).(*VirtualMachine)
// This task creates multiple subtasks which violates the assumption
// of 1:1 Context:Task, which results in data races in objects
// like the Simulator.Event manager. This is the minimum context
// required for the PowerOnVMTask to complete.
taskCtx := &Context{
Context: ctx.Context,
Session: ctx.Session,
Map: ctx.Map,
}
// NOTE: Simulator does not actually perform any specific host-level placement
// (equivalent to vSphere DRS).
taskCtx.WithLock(vm, func() {
vmTaskBody := vm.PowerOnVMTask(taskCtx, &types.PowerOnVM_Task{}).(*methods.PowerOnVM_TaskBody)
res.Attempted = append(res.Attempted, types.ClusterAttemptedVmInfo{Vm: ref, Task: &vmTaskBody.Res.Returnval})
})
}
return nil, nil
return res, nil
})
return &methods.PowerOnMultiVM_TaskBody{
Res: &types.PowerOnMultiVM_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}
func (d *Datacenter) DestroyTask(req *types.Destroy_Task) soap.HasFault {
func (d *Datacenter) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
task := CreateTask(d, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
folders := []types.ManagedObjectReference{
d.VmFolder,
@@ -142,19 +200,21 @@ func (d *Datacenter) DestroyTask(req *types.Destroy_Task) soap.HasFault {
}
for _, ref := range folders {
if len(Map.Get(ref).(*Folder).ChildEntity) != 0 {
f, _ := asFolderMO(ctx.Map.Get(ref))
if len(f.ChildEntity) != 0 {
return nil, &types.ResourceInUse{}
}
}
Map.Get(*d.Parent).(*Folder).removeChild(d.Self)
p, _ := asFolderMO(ctx.Map.Get(*d.Parent))
folderRemoveChild(ctx, p, d.Self)
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}

View File

@@ -17,6 +17,10 @@ limitations under the License.
package simulator
import (
"net/url"
"os"
"path"
"strings"
"time"
"github.com/vmware/govmomi/object"
@@ -30,6 +34,32 @@ type Datastore struct {
mo.Datastore
}
func (ds *Datastore) eventArgument() *types.DatastoreEventArgument {
return &types.DatastoreEventArgument{
Datastore: ds.Self,
EntityEventArgument: types.EntityEventArgument{Name: ds.Name},
}
}
func (ds *Datastore) model(m *Model) error {
info := ds.Info.GetDatastoreInfo()
u, _ := url.Parse(info.Url)
if u.Scheme == "ds" {
// rewrite saved vmfs path to a local temp dir
u.Path = path.Clean(u.Path)
parent := strings.ReplaceAll(path.Dir(u.Path), "/", "_")
name := strings.ReplaceAll(path.Base(u.Path), ":", "_")
dir, err := m.createTempDir(parent, name)
if err != nil {
return err
}
info.Url = dir
}
return nil
}
func parseDatastorePath(dsPath string) (*object.DatastorePath, types.BaseMethodFault) {
var p object.DatastorePath
@@ -43,7 +73,7 @@ func parseDatastorePath(dsPath string) (*object.DatastorePath, types.BaseMethodF
func (ds *Datastore) RefreshDatastore(*types.RefreshDatastore) soap.HasFault {
r := &methods.RefreshDatastoreBody{}
err := ds.stat()
_, err := os.Stat(ds.Info.GetDatastoreInfo().Url)
if err != nil {
r.Fault_ = Fault(err.Error(), &types.HostConfigFault{})
return r
@@ -51,9 +81,36 @@ func (ds *Datastore) RefreshDatastore(*types.RefreshDatastore) soap.HasFault {
info := ds.Info.GetDatastoreInfo()
now := time.Now()
info.Timestamp = &now
info.Timestamp = types.NewTime(time.Now())
return r
}
func (ds *Datastore) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
task := CreateTask(ds, "destroy", func(*Task) (types.AnyType, types.BaseMethodFault) {
if len(ds.Vm) != 0 {
return nil, &types.ResourceInUse{
Type: ds.Self.Type,
Name: ds.Name,
}
}
for _, mount := range ds.Host {
host := ctx.Map.Get(mount.Key).(*HostSystem)
ctx.Map.RemoveReference(ctx, host, &host.Datastore, ds.Self)
parent := hostParent(&host.HostSystem)
ctx.Map.RemoveReference(ctx, parent, &parent.Datastore, ds.Self)
}
p, _ := asFolderMO(ctx.Map.Get(*ds.Parent))
folderRemoveChild(ctx, p, ds.Self)
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(ctx),
},
}
}

View File

@@ -17,6 +17,12 @@ limitations under the License.
package simulator
import (
"fmt"
"strconv"
"strings"
"github.com/google/uuid"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
@@ -25,11 +31,13 @@ import (
type DistributedVirtualSwitch struct {
mo.DistributedVirtualSwitch
types.FetchDVPortsResponse
}
func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Task) soap.HasFault {
func (s *DistributedVirtualSwitch) AddDVPortgroupTask(ctx *Context, c *types.AddDVPortgroup_Task) soap.HasFault {
task := CreateTask(s, "addDVPortgroup", func(t *Task) (types.AnyType, types.BaseMethodFault) {
f := Map.getEntityParent(s, "Folder").(*Folder)
f := ctx.Map.getEntityParent(s, "Folder").(*Folder)
portgroups := s.Portgroup
portgroupNames := s.Summary.PortgroupName
@@ -39,14 +47,26 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Ta
pg.Name = spec.Name
pg.Entity().Name = pg.Name
if obj := Map.FindByName(pg.Name, f.ChildEntity); obj != nil {
return nil, &types.DuplicateName{
Name: pg.Name,
Object: obj.Reference(),
// Standard AddDVPortgroupTask() doesn't allow duplicate names, but NSX 3.0 does create some DVPGs with the same name.
// Allow duplicate names using this prefix so we can reproduce and test this condition.
if strings.HasPrefix(pg.Name, "NSX-") || spec.BackingType == string(types.DistributedVirtualPortgroupBackingTypeNsx) {
if spec.LogicalSwitchUuid == "" {
spec.LogicalSwitchUuid = uuid.New().String()
}
if spec.SegmentId == "" {
spec.SegmentId = fmt.Sprintf("/infra/segments/vnet_%s", uuid.New().String())
}
} else {
if obj := ctx.Map.FindByName(pg.Name, f.ChildEntity); obj != nil {
return nil, &types.DuplicateName{
Name: pg.Name,
Object: obj.Reference(),
}
}
}
f.putChild(pg)
folderPutChild(ctx, &f.Folder, pg)
pg.Key = pg.Self.Value
pg.Config = types.DVPortgroupConfigInfo{
@@ -64,15 +84,58 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Ta
ConfigVersion: spec.ConfigVersion,
AutoExpand: spec.AutoExpand,
VmVnicNetworkResourcePoolKey: spec.VmVnicNetworkResourcePoolKey,
LogicalSwitchUuid: spec.LogicalSwitchUuid,
SegmentId: spec.SegmentId,
BackingType: spec.BackingType,
}
if pg.Config.LogicalSwitchUuid != "" {
if pg.Config.BackingType == "" {
pg.Config.BackingType = "nsx"
}
}
if pg.Config.DefaultPortConfig == nil {
pg.Config.DefaultPortConfig = &types.VMwareDVSPortSetting{
Vlan: new(types.VmwareDistributedVirtualSwitchVlanIdSpec),
UplinkTeamingPolicy: &types.VmwareUplinkPortTeamingPolicy{
Policy: &types.StringPolicy{
Value: "loadbalance_srcid",
},
ReversePolicy: &types.BoolPolicy{
Value: types.NewBool(true),
},
NotifySwitches: &types.BoolPolicy{
Value: types.NewBool(true),
},
RollingOrder: &types.BoolPolicy{
Value: types.NewBool(true),
},
},
}
}
pg.PortKeys = []string{}
if pg.Config.Policy == nil {
pg.Config.Policy = &types.VMwareDVSPortgroupPolicy{
DVPortgroupPolicy: types.DVPortgroupPolicy{
BlockOverrideAllowed: true,
ShapingOverrideAllowed: false,
VendorConfigOverrideAllowed: false,
LivePortMovingAllowed: false,
PortConfigResetAtDisconnect: true,
NetworkResourcePoolOverrideAllowed: types.NewBool(false),
TrafficFilterOverrideAllowed: types.NewBool(false),
},
VlanOverrideAllowed: false,
UplinkTeamingOverrideAllowed: false,
SecurityPolicyOverrideAllowed: false,
IpfixOverrideAllowed: types.NewBool(false),
}
}
for i := 0; i < int(spec.NumPorts); i++ {
pg.PortKeys = append(pg.PortKeys, strconv.Itoa(i))
}
portgroups = append(portgroups, pg.Self)
portgroupNames = append(portgroupNames, pg.Name)
@@ -80,18 +143,18 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Ta
for _, h := range s.Summary.HostMember {
pg.Host = append(pg.Host, h)
host := Map.Get(h).(*HostSystem)
Map.AppendReference(host, &host.Network, pg.Reference())
host := ctx.Map.Get(h).(*HostSystem)
ctx.Map.AppendReference(ctx, host, &host.Network, pg.Reference())
parent := Map.Get(*host.HostSystem.Parent)
parent := ctx.Map.Get(*host.HostSystem.Parent)
computeNetworks := append(hostParent(&host.HostSystem).Network, pg.Reference())
Map.Update(parent, []types.PropertyChange{
ctx.Map.Update(parent, []types.PropertyChange{
{Name: "network", Val: computeNetworks},
})
}
}
Map.Update(s, []types.PropertyChange{
ctx.Map.Update(s, []types.PropertyChange{
{Name: "portgroup", Val: portgroups},
{Name: "summary.portgroupName", Val: portgroupNames},
})
@@ -101,19 +164,19 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Ta
return &methods.AddDVPortgroup_TaskBody{
Res: &types.AddDVPortgroup_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}
func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_Task) soap.HasFault {
func (s *DistributedVirtualSwitch) ReconfigureDvsTask(ctx *Context, req *types.ReconfigureDvs_Task) soap.HasFault {
task := CreateTask(s, "reconfigureDvs", func(t *Task) (types.AnyType, types.BaseMethodFault) {
spec := req.Spec.GetDVSConfigSpec()
members := s.Summary.HostMember
for _, member := range spec.Host {
h := Map.Get(member.Host)
h := ctx.Map.Get(member.Host)
if h == nil {
return nil, &types.ManagedObjectNotFound{Obj: member.Host}
}
@@ -127,29 +190,34 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
}
hostNetworks := append(host.Network, s.Portgroup...)
Map.Update(host, []types.PropertyChange{
ctx.Map.Update(host, []types.PropertyChange{
{Name: "network", Val: hostNetworks},
})
members = append(members, member.Host)
parent := Map.Get(*host.HostSystem.Parent)
parent := ctx.Map.Get(*host.HostSystem.Parent)
var pgs []types.ManagedObjectReference
for _, ref := range s.Portgroup {
pg := Map.Get(ref).(*DistributedVirtualPortgroup)
pg := ctx.Map.Get(ref).(*DistributedVirtualPortgroup)
pgs = append(pgs, ref)
pgHosts := append(pg.Host, member.Host)
Map.Update(pg, []types.PropertyChange{
ctx.Map.Update(pg, []types.PropertyChange{
{Name: "host", Val: pgHosts},
})
cr := hostParent(&host.HostSystem)
if FindReference(cr.Network, ref) == nil {
computeNetworks := append(cr.Network, ref)
ctx.Map.Update(parent, []types.PropertyChange{
{Name: "network", Val: computeNetworks},
})
}
}
Map.Update(parent, []types.PropertyChange{
{Name: "network", Val: pgs},
})
case types.ConfigSpecOperationRemove:
for _, ref := range host.Vm {
vm := Map.Get(ref).(*VirtualMachine)
vm := ctx.Map.Get(ref).(*VirtualMachine)
if pg := FindReference(vm.Network, s.Portgroup...); pg != nil {
return nil, &types.ResourceInUse{
Type: pg.Type,
@@ -164,7 +232,7 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
}
}
Map.Update(s, []types.PropertyChange{
ctx.Map.Update(s, []types.PropertyChange{
{Name: "summary.hostMember", Val: members},
})
@@ -173,7 +241,7 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
return &methods.ReconfigureDvs_TaskBody{
Res: &types.ReconfigureDvs_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}
@@ -186,42 +254,142 @@ func (s *DistributedVirtualSwitch) FetchDVPorts(req *types.FetchDVPorts) soap.Ha
return body
}
func (s *DistributedVirtualSwitch) DestroyTask(req *types.Destroy_Task) soap.HasFault {
func (s *DistributedVirtualSwitch) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
task := CreateTask(s, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
f := Map.getEntityParent(s, "Folder").(*Folder)
f.removeChild(s.Reference())
f := ctx.Map.getEntityParent(s, "Folder").(*Folder)
folderRemoveChild(ctx, &f.Folder, s.Reference())
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}
func (s *DistributedVirtualSwitch) dvPortgroups(_ *types.DistributedVirtualSwitchPortCriteria) []types.DistributedVirtualPort {
// TODO(agui): Filter is not implemented yet
var res []types.DistributedVirtualPort
func (s *DistributedVirtualSwitch) dvPortgroups(criteria *types.DistributedVirtualSwitchPortCriteria) []types.DistributedVirtualPort {
res := s.FetchDVPortsResponse.Returnval
if len(res) != 0 {
return res
}
for _, ref := range s.Portgroup {
pg := Map.Get(ref).(*DistributedVirtualPortgroup)
res = append(res, types.DistributedVirtualPort{
DvsUuid: s.Uuid,
Key: pg.Key,
Config: types.DVPortConfigInfo{
Setting: pg.Config.DefaultPortConfig,
},
})
for _, key := range pg.PortKeys {
res = append(res, types.DistributedVirtualPort{
DvsUuid: s.Uuid,
Key: key,
DvsUuid: s.Uuid,
Key: key,
PortgroupKey: pg.Key,
Config: types.DVPortConfigInfo{
Setting: pg.Config.DefaultPortConfig,
},
})
}
}
// filter ports by criteria
res = s.filterDVPorts(res, criteria)
return res
}
func (s *DistributedVirtualSwitch) filterDVPorts(
ports []types.DistributedVirtualPort,
criteria *types.DistributedVirtualSwitchPortCriteria,
) []types.DistributedVirtualPort {
if criteria == nil {
return ports
}
ports = s.filterDVPortsByPortgroupKey(ports, criteria)
ports = s.filterDVPortsByPortKey(ports, criteria)
ports = s.filterDVPortsByConnected(ports, criteria)
return ports
}
func (s *DistributedVirtualSwitch) filterDVPortsByPortgroupKey(
ports []types.DistributedVirtualPort,
criteria *types.DistributedVirtualSwitchPortCriteria,
) []types.DistributedVirtualPort {
if len(criteria.PortgroupKey) == 0 || criteria.Inside == nil {
return ports
}
// inside portgroup keys
if *criteria.Inside {
filtered := []types.DistributedVirtualPort{}
for _, p := range ports {
for _, pgk := range criteria.PortgroupKey {
if p.PortgroupKey == pgk {
filtered = append(filtered, p)
break
}
}
}
return filtered
}
// outside portgroup keys
filtered := []types.DistributedVirtualPort{}
for _, p := range ports {
found := false
for _, pgk := range criteria.PortgroupKey {
if p.PortgroupKey == pgk {
found = true
break
}
}
if !found {
filtered = append(filtered, p)
}
}
return filtered
}
func (s *DistributedVirtualSwitch) filterDVPortsByPortKey(
ports []types.DistributedVirtualPort,
criteria *types.DistributedVirtualSwitchPortCriteria,
) []types.DistributedVirtualPort {
if len(criteria.PortKey) == 0 {
return ports
}
filtered := []types.DistributedVirtualPort{}
for _, p := range ports {
for _, pk := range criteria.PortKey {
if p.Key == pk {
filtered = append(filtered, p)
break
}
}
}
return filtered
}
func (s *DistributedVirtualSwitch) filterDVPortsByConnected(
ports []types.DistributedVirtualPort,
criteria *types.DistributedVirtualSwitchPortCriteria,
) []types.DistributedVirtualPort {
if criteria.Connected == nil {
return ports
}
filtered := []types.DistributedVirtualPort{}
for _, p := range ports {
connected := p.Connectee != nil
if connected == *criteria.Connected {
filtered = append(filtered, p)
}
}
return filtered
}

View File

@@ -0,0 +1,51 @@
/*
Copyright (c) 2020 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type DistributedVirtualSwitchManager struct {
mo.DistributedVirtualSwitchManager
}
func (m *DistributedVirtualSwitchManager) DVSManagerLookupDvPortGroup(ctx *Context, req *types.DVSManagerLookupDvPortGroup) soap.HasFault {
body := &methods.DVSManagerLookupDvPortGroupBody{}
for _, obj := range ctx.Map.All("DistributedVirtualSwitch") {
dvs := obj.(*DistributedVirtualSwitch)
if dvs.Uuid == req.SwitchUuid {
for _, ref := range dvs.Portgroup {
pg := ctx.Map.Get(ref).(*DistributedVirtualPortgroup)
if pg.Key == req.PortgroupKey {
body.Res = &types.DVSManagerLookupDvPortGroupResponse{
Returnval: &ref,
}
return body
}
}
}
}
body.Fault_ = Fault("", new(types.NotFound))
return body
}

View File

@@ -23,24 +23,25 @@ import (
"github.com/vmware/govmomi/vim25/types"
)
func RenameTask(e mo.Entity, r *types.Rename_Task) soap.HasFault {
func RenameTask(ctx *Context, e mo.Entity, r *types.Rename_Task, dup ...bool) soap.HasFault {
task := CreateTask(e, "rename", func(t *Task) (types.AnyType, types.BaseMethodFault) {
obj := Map.Get(r.This).(mo.Entity).Entity()
obj := ctx.Map.Get(r.This).(mo.Entity).Entity()
if parent, ok := Map.Get(*obj.Parent).(*Folder); ok {
if Map.FindByName(r.NewName, parent.ChildEntity) != nil {
canDup := len(dup) == 1 && dup[0]
if parent, ok := asFolderMO(ctx.Map.Get(*obj.Parent)); ok && !canDup {
if ctx.Map.FindByName(r.NewName, parent.ChildEntity) != nil {
return nil, &types.InvalidArgument{InvalidProperty: "name"}
}
}
Map.Update(e, []types.PropertyChange{{Name: "name", Val: r.NewName}})
ctx.Map.Update(e, []types.PropertyChange{{Name: "name", Val: r.NewName}})
return nil, nil
})
return &methods.Rename_TaskBody{
Res: &types.Rename_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}

View File

@@ -17,6 +17,8 @@ limitations under the License.
package simulator
import (
"strings"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
@@ -26,6 +28,8 @@ import (
type EnvironmentBrowser struct {
mo.EnvironmentBrowser
types.QueryConfigOptionResponse
}
func newEnvironmentBrowser() *types.ManagedObjectReference {
@@ -34,12 +38,33 @@ func newEnvironmentBrowser() *types.ManagedObjectReference {
return &env.Self
}
func (b *EnvironmentBrowser) hosts(ctx *Context) []types.ManagedObjectReference {
ctx.Map.m.Lock()
defer ctx.Map.m.Unlock()
for _, obj := range ctx.Map.objects {
switch e := obj.(type) {
case *mo.ComputeResource:
if b.Self == *e.EnvironmentBrowser {
return e.Host
}
case *ClusterComputeResource:
if b.Self == *e.EnvironmentBrowser {
return e.Host
}
}
}
return nil
}
func (b *EnvironmentBrowser) QueryConfigOption(req *types.QueryConfigOption) soap.HasFault {
body := new(methods.QueryConfigOptionBody)
opt := &types.VirtualMachineConfigOption{
Version: esx.HardwareVersion,
DefaultDevice: esx.VirtualDevice,
opt := b.QueryConfigOptionResponse.Returnval
if opt == nil {
opt = &types.VirtualMachineConfigOption{
Version: esx.HardwareVersion,
DefaultDevice: esx.VirtualDevice,
}
}
body.Res = &types.QueryConfigOptionResponse{
@@ -49,12 +74,56 @@ func (b *EnvironmentBrowser) QueryConfigOption(req *types.QueryConfigOption) soa
return body
}
func guestFamily(id string) string {
// TODO: We could capture the entire GuestOsDescriptor list from EnvironmentBrowser,
// but it is a ton of data.. this should be good enough for now.
switch {
case strings.HasPrefix(id, "win"):
return string(types.VirtualMachineGuestOsFamilyWindowsGuest)
case strings.HasPrefix(id, "darwin"):
return string(types.VirtualMachineGuestOsFamilyDarwinGuestFamily)
default:
return string(types.VirtualMachineGuestOsFamilyLinuxGuest)
}
}
func (b *EnvironmentBrowser) QueryConfigOptionEx(req *types.QueryConfigOptionEx) soap.HasFault {
body := new(methods.QueryConfigOptionExBody)
opt := &types.VirtualMachineConfigOption{
Version: esx.HardwareVersion,
DefaultDevice: esx.VirtualDevice,
opt := b.QueryConfigOptionResponse.Returnval
if opt == nil {
opt = &types.VirtualMachineConfigOption{
Version: esx.HardwareVersion,
DefaultDevice: esx.VirtualDevice,
}
}
if req.Spec != nil {
// From the SDK QueryConfigOptionEx doc:
// "If guestId is nonempty, the guestOSDescriptor array of the config option is filtered to match against the guest IDs in the spec.
// If there is no match, the whole list is returned."
for _, id := range req.Spec.GuestId {
for _, gid := range GuestID {
if string(gid) == id {
opt.GuestOSDescriptor = []types.GuestOsDescriptor{{
Id: id,
Family: guestFamily(id),
}}
break
}
}
}
}
if len(opt.GuestOSDescriptor) == 0 {
for i := range GuestID {
id := string(GuestID[i])
opt.GuestOSDescriptor = append(opt.GuestOSDescriptor, types.GuestOsDescriptor{
Id: id,
Family: guestFamily(id),
})
}
}
body.Res = &types.QueryConfigOptionExResponse{
@@ -63,3 +132,104 @@ func (b *EnvironmentBrowser) QueryConfigOptionEx(req *types.QueryConfigOptionEx)
return body
}
func (b *EnvironmentBrowser) QueryConfigOptionDescriptor(ctx *Context, req *types.QueryConfigOptionDescriptor) soap.HasFault {
body := &methods.QueryConfigOptionDescriptorBody{
Res: new(types.QueryConfigOptionDescriptorResponse),
}
body.Res.Returnval = []types.VirtualMachineConfigOptionDescriptor{{
Key: esx.HardwareVersion,
Description: esx.HardwareVersion,
Host: b.hosts(ctx),
CreateSupported: types.NewBool(true),
DefaultConfigOption: types.NewBool(false),
RunSupported: types.NewBool(true),
UpgradeSupported: types.NewBool(true),
}}
return body
}
func (b *EnvironmentBrowser) QueryConfigTarget(ctx *Context, req *types.QueryConfigTarget) soap.HasFault {
body := &methods.QueryConfigTargetBody{
Res: &types.QueryConfigTargetResponse{
Returnval: &types.ConfigTarget{
SmcPresent: types.NewBool(false),
},
},
}
target := body.Res.Returnval
var hosts []types.ManagedObjectReference
if req.Host == nil {
hosts = b.hosts(ctx)
} else {
hosts = append(hosts, *req.Host)
}
seen := make(map[types.ManagedObjectReference]bool)
for i := range hosts {
host := ctx.Map.Get(hosts[i]).(*HostSystem)
target.NumCpus += int32(host.Summary.Hardware.NumCpuPkgs)
target.NumCpuCores += int32(host.Summary.Hardware.NumCpuCores)
target.NumNumaNodes++
for _, ref := range host.Datastore {
if seen[ref] {
continue
}
seen[ref] = true
ds := ctx.Map.Get(ref).(*Datastore)
target.Datastore = append(target.Datastore, types.VirtualMachineDatastoreInfo{
VirtualMachineTargetInfo: types.VirtualMachineTargetInfo{
Name: ds.Name,
},
Datastore: ds.Summary,
Capability: ds.Capability,
Mode: string(types.HostMountModeReadWrite),
VStorageSupport: string(types.FileSystemMountInfoVStorageSupportStatusVStorageUnsupported),
})
}
for _, ref := range host.Network {
if seen[ref] {
continue
}
seen[ref] = true
switch n := ctx.Map.Get(ref).(type) {
case *mo.Network:
target.Network = append(target.Network, types.VirtualMachineNetworkInfo{
VirtualMachineTargetInfo: types.VirtualMachineTargetInfo{
Name: n.Name,
},
Network: n.Summary.GetNetworkSummary(),
})
case *DistributedVirtualPortgroup:
dvs := ctx.Map.Get(*n.Config.DistributedVirtualSwitch).(*DistributedVirtualSwitch)
target.DistributedVirtualPortgroup = append(target.DistributedVirtualPortgroup, types.DistributedVirtualPortgroupInfo{
SwitchName: dvs.Name,
SwitchUuid: dvs.Uuid,
PortgroupName: n.Name,
PortgroupKey: n.Key,
PortgroupType: n.Config.Type,
UplinkPortgroup: false,
Portgroup: n.Self,
NetworkReservationSupported: types.NewBool(false),
})
case *DistributedVirtualSwitch:
target.DistributedVirtualSwitch = append(target.DistributedVirtualSwitch, types.DistributedVirtualSwitchInfo{
SwitchName: n.Name,
SwitchUuid: n.Uuid,
DistributedVirtualSwitch: n.Self,
NetworkReservationSupported: types.NewBool(false),
})
}
}
}
return body
}

File diff suppressed because one or more lines are too long

View File

@@ -93,7 +93,7 @@ var EventInfo = []types.EventDescriptionEventDetail{
Key: "VmMigratedEvent",
Description: "VM migrated",
Category: "info",
FullFormat: "Migration of virtual machine {{.Vm.Name}} from {{.SourceHost.Name}, {{.SourceDatastore.Name}} to {{.Host.Name}, {{.Ds.Name}} completed",
FullFormat: "Migration of virtual machine {{.Vm.Name}} from {{.SourceHost.Name}}, {{.SourceDatastore.Name}} to {{.Host.Name}}, {{.Ds.Name}} completed",
},
{
Key: "VmBeingMigratedEvent",
@@ -233,6 +233,24 @@ var EventInfo = []types.EventDescriptionEventDetail{
Category: "info",
FullFormat: "Completed the relocation of the virtual machine",
},
{
Key: "CustomizationFailed",
Description: "An error occurred during customization",
Category: "info",
FullFormat: "An error occurred during customization on VM {{.Vm.Name}}",
},
{
Key: "CustomizationStartedEvent",
Description: "Started customization",
Category: "info",
FullFormat: "Started customization of VM {{.Vm.Name}}",
},
{
Key: "CustomizationSucceeded",
Description: "Customization succeeded",
Category: "info",
FullFormat: "Customization of VM {{.Vm.Name}} succeeded",
},
{
Key: "DrsVmMigratedEvent",
Description: "DRS VM migrated",

View File

@@ -544,6 +544,30 @@ var HostConfigInfo = types.HostConfigInfo{
NicType: "management",
MultiSelectAllowed: true,
CandidateVnic: []types.HostVirtualNic{
{
Device: "vmk1",
Key: "management.key-vim.host.VirtualNic-vmk1",
Portgroup: "",
Spec: types.HostVirtualNicSpec{
Ip: &types.HostIpConfig{
Dhcp: true,
IpAddress: "192.168.0.1",
SubnetMask: "255.0.0.0",
IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil),
},
Mac: "00:0c:29:81:d8:00",
DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil),
Portgroup: "Management Network",
Mtu: 1500,
TsoEnabled: types.NewBool(true),
NetStackInstanceKey: "defaultTcpipStack",
OpaqueNetwork: (*types.HostVirtualNicOpaqueNetworkSpec)(nil),
ExternalId: "",
PinnedPnic: "",
IpRouteSpec: (*types.HostVirtualNicIpRouteSpec)(nil),
},
Port: "",
},
{
Device: "vmk0",
Key: "management.key-vim.host.VirtualNic-vmk0",

View File

@@ -1740,9 +1740,19 @@ var HostSystem = mo.HostSystem{
CurrentEVCModeKey: "",
Gateway: (*types.HostListSummaryGatewaySummary)(nil),
},
Hardware: (*types.HostHardwareInfo)(nil),
Capability: (*types.HostCapability)(nil),
LicensableResource: types.HostLicensableResourceInfo{},
Hardware: (*types.HostHardwareInfo)(nil),
Capability: (*types.HostCapability)(nil),
LicensableResource: types.HostLicensableResourceInfo{
Resource: []types.KeyAnyValue{
{
Key: "numCpuPackages",
Value: types.KeyValue{
Key: "numCpuPackages",
Value: "2",
},
},
},
},
ConfigManager: types.HostConfigManager{
DynamicData: types.DynamicData{},
CpuScheduler: &types.ManagedObjectReference{Type: "HostCpuSchedulerSystem", Value: "cpuSchedulerSystem"},

View File

@@ -9735,7 +9735,7 @@ var Description = types.TaskDescription{
&types.ElementDescription{
Description: types.Description{
Label: "Retrieve Managed Method Executer",
Summary: "Retrieves a referemce to Managed Method Executer",
Summary: "Retrieves a reference to Managed Method Executer",
},
Key: "HostSystem.retrieveManagedMethodExecuter",
},
@@ -9819,7 +9819,7 @@ var Description = types.TaskDescription{
&types.ElementDescription{
Description: types.Description{
Label: "Query feature capabilities for vSphere Distributed Switch specification",
Summary: "Queries feature capabilites available for a given vSphere Distributed Switch specification",
Summary: "Queries feature capabilities available for a given vSphere Distributed Switch specification",
},
Key: "dvs.DistributedVirtualSwitchManager.queryFeatureCapability",
},

View File

@@ -18,13 +18,12 @@ package simulator
import (
"bytes"
"container/ring"
"container/list"
"log"
"reflect"
"text/template"
"time"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
@@ -40,27 +39,26 @@ var (
type EventManager struct {
mo.EventManager
root types.ManagedObjectReference
page *ring.Ring
key int32
root types.ManagedObjectReference
history *list.List
key int32
collectors map[types.ManagedObjectReference]*EventHistoryCollector
templates map[string]*template.Template
}
func NewEventManager(ref types.ManagedObjectReference) object.Reference {
return &EventManager{
EventManager: mo.EventManager{
Self: ref,
Description: types.EventDescription{
EventInfo: esx.EventInfo,
},
MaxCollector: 1000,
},
root: Map.content().RootFolder,
page: ring.New(maxPageSize),
collectors: make(map[types.ManagedObjectReference]*EventHistoryCollector),
templates: make(map[string]*template.Template),
func (m *EventManager) init(r *Registry) {
if len(m.Description.EventInfo) == 0 {
m.Description.EventInfo = esx.EventInfo
}
if m.MaxCollector == 0 {
m.MaxCollector = 1000
}
m.root = r.content().RootFolder
m.history = list.New()
m.collectors = make(map[types.ManagedObjectReference]*EventHistoryCollector)
m.templates = make(map[string]*template.Template)
}
func (m *EventManager) createCollector(ctx *Context, req *types.CreateCollectorForEvents) (*EventHistoryCollector, *soap.Fault) {
@@ -75,10 +73,11 @@ func (m *EventManager) createCollector(ctx *Context, req *types.CreateCollectorF
collector := &EventHistoryCollector{
m: m,
page: ring.New(size),
page: list.New(),
size: size,
}
collector.Filter = req.Filter
collector.fillPage(size)
collector.fillPage()
return collector, nil
}
@@ -151,6 +150,13 @@ func (m *EventManager) formatMessage(event types.BaseEvent) {
}
}
func pushEvent(l *list.List, event types.BaseEvent) {
if l.Len() > maxPageSize*5 {
l.Remove(l.Front()) // Prune history
}
l.PushBack(event)
}
func (m *EventManager) PostEvent(ctx *Context, req *types.PostEvent) soap.HasFault {
m.key++
event := req.EventToPost.GetEvent()
@@ -159,15 +165,14 @@ func (m *EventManager) PostEvent(ctx *Context, req *types.PostEvent) soap.HasFau
event.CreatedTime = time.Now()
event.UserName = ctx.Session.UserName
m.page = m.page.Prev()
m.page.Value = req.EventToPost
m.formatMessage(req.EventToPost)
pushEvent(m.history, req.EventToPost)
for _, c := range m.collectors {
ctx.WithLock(c, func() {
if c.eventMatches(req.EventToPost) {
c.page = c.page.Prev()
c.page.Value = req.EventToPost
pushEvent(c.page, req.EventToPost)
Map.Update(c, []types.PropertyChange{{Name: "latestPage", Val: c.GetLatestPage()}})
}
})
@@ -182,8 +187,9 @@ type EventHistoryCollector struct {
mo.EventHistoryCollector
m *EventManager
page *ring.Ring
pos int
size int
page *list.List
pos *list.Element
}
// doEntityEventArgument calls f for each entity argument in the event.
@@ -316,54 +322,55 @@ func (c *EventHistoryCollector) typeMatches(event types.BaseEvent, spec *types.E
return false
}
func (c *EventHistoryCollector) timeMatches(event types.BaseEvent, spec *types.EventFilterSpec) bool {
if spec.Time == nil {
return true
}
created := event.GetEvent().CreatedTime
if begin := spec.Time.BeginTime; begin != nil {
if created.Before(*begin) {
return false
}
}
if end := spec.Time.EndTime; end != nil {
if created.After(*end) {
return false
}
}
return true
}
// eventMatches returns true one of the filters matches the event.
func (c *EventHistoryCollector) eventMatches(event types.BaseEvent) bool {
spec := c.Filter.(types.EventFilterSpec)
if !c.typeMatches(event, &spec) {
return false
matchers := []func(types.BaseEvent, *types.EventFilterSpec) bool{
c.typeMatches,
c.timeMatches,
c.entityMatches,
// TODO: spec.UserName, etc
}
// TODO: spec.Time, spec.UserName, etc
for _, match := range matchers {
if !match(event, &spec) {
return false
}
}
return c.entityMatches(event, &spec)
return true
}
// filePage copies the manager's latest events into the collector's page with Filter applied.
func (c *EventHistoryCollector) fillPage(size int) {
c.pos = 0
l := c.page.Len()
delta := size - l
if delta < 0 {
// Shrink ring size
c.page = c.page.Unlink(-delta)
return
}
matches := 0
mpage := c.m.page
page := c.page
if delta != 0 {
// Grow ring size
c.page = c.page.Link(ring.New(delta))
}
for i := 0; i < maxPageSize; i++ {
event, ok := mpage.Value.(types.BaseEvent)
mpage = mpage.Prev()
if !ok {
continue
}
// fillPage copies the manager's latest events into the collector's page with Filter applied.
func (c *EventHistoryCollector) fillPage() {
for e := c.m.history.Front(); e != nil; e = e.Next() {
event := e.Value.(types.BaseEvent)
if c.eventMatches(event) {
page.Value = event
page = page.Prev()
matches++
if matches == size {
break
}
c.page.PushBack(event)
}
}
}
@@ -388,21 +395,47 @@ func (c *EventHistoryCollector) SetCollectorPageSize(ctx *Context, req *types.Se
return body
}
ctx.WithLock(c.m, func() {
c.fillPage(size)
})
c.size = size
c.page = list.New()
ctx.WithLock(c.m, c.fillPage)
body.Res = new(types.SetCollectorPageSizeResponse)
return body
}
func (c *EventHistoryCollector) ResetCollector(ctx *Context, req *types.ResetCollector) soap.HasFault {
c.pos = c.page.Back()
return &methods.ResetCollectorBody{
Res: new(types.ResetCollectorResponse),
}
}
func (c *EventHistoryCollector) RewindCollector(ctx *Context, req *types.RewindCollector) soap.HasFault {
c.pos = 0
c.pos = c.page.Front()
return &methods.RewindCollectorBody{
Res: new(types.RewindCollectorResponse),
}
}
// readEvents returns the next max Events from the EventManager's history
func (c *EventHistoryCollector) readEvents(ctx *Context, max int32, next func() *list.Element) []types.BaseEvent {
var events []types.BaseEvent
for i := 0; i < int(max); i++ {
e := next()
if e == nil {
break
}
events = append(events, e.Value.(types.BaseEvent))
c.pos = e
}
return events
}
func (c *EventHistoryCollector) ReadNextEvents(ctx *Context, req *types.ReadNextEvents) soap.HasFault {
body := &methods.ReadNextEventsBody{}
if req.MaxCount <= 0 {
@@ -411,21 +444,14 @@ func (c *EventHistoryCollector) ReadNextEvents(ctx *Context, req *types.ReadNext
}
body.Res = new(types.ReadNextEventsResponse)
events := c.GetLatestPage()
nevents := len(events)
if c.pos == nevents {
return body // already read to EOF
next := func() *list.Element {
if c.pos != nil {
return c.pos.Next()
}
return c.page.Front()
}
start := c.pos
end := start + int(req.MaxCount)
c.pos += int(req.MaxCount)
if end > nevents {
end = nevents
c.pos = nevents
}
body.Res.Returnval = events[start:end]
body.Res.Returnval = c.readEvents(ctx, req.MaxCount, next)
return body
}
@@ -438,26 +464,20 @@ func (c *EventHistoryCollector) ReadPreviousEvents(ctx *Context, req *types.Read
}
body.Res = new(types.ReadPreviousEventsResponse)
events := c.GetLatestPage()
if c.pos == 0 {
return body // already read to EOF
next := func() *list.Element {
if c.pos != nil {
return c.pos.Prev()
}
return c.page.Back()
}
start := c.pos - int(req.MaxCount)
end := c.pos
c.pos -= int(req.MaxCount)
if start < 0 {
start = 0
c.pos = 0
}
body.Res.Returnval = events[start:end]
body.Res.Returnval = c.readEvents(ctx, req.MaxCount, next)
return body
}
func (c *EventHistoryCollector) DestroyCollector(ctx *Context, req *types.DestroyCollector) soap.HasFault {
ctx.Session.Remove(req.This)
ctx.Session.Remove(ctx, req.This)
ctx.WithLock(c.m, func() {
delete(c.m.collectors, req.This)
@@ -471,12 +491,14 @@ func (c *EventHistoryCollector) DestroyCollector(ctx *Context, req *types.Destro
func (c *EventHistoryCollector) GetLatestPage() []types.BaseEvent {
var latestPage []types.BaseEvent
c.page.Do(func(val interface{}) {
if val == nil {
return
e := c.page.Back()
for i := 0; i < c.size; i++ {
if e == nil {
break
}
latestPage = append(latestPage, val.(types.BaseEvent))
})
latestPage = append(latestPage, e.Value.(types.BaseEvent))
e = e.Prev()
}
return latestPage
}

View File

@@ -20,8 +20,8 @@ import (
"io"
"os"
"path"
"path/filepath"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
@@ -33,30 +33,30 @@ type FileManager struct {
mo.FileManager
}
func NewFileManager(ref types.ManagedObjectReference) object.Reference {
m := &FileManager{}
m.Self = ref
return m
}
func (f *FileManager) findDatastore(ref mo.Reference, name string) (*Datastore, types.BaseMethodFault) {
var refs []types.ManagedObjectReference
switch obj := ref.(type) {
case *Folder:
refs = obj.ChildEntity
case *StoragePod:
refs = obj.ChildEntity
if d, ok := asFolderMO(ref); ok {
refs = d.ChildEntity
}
if p, ok := ref.(*StoragePod); ok {
refs = p.ChildEntity
}
for _, ref := range refs {
switch obj := Map.Get(ref).(type) {
case *Datastore:
if obj.Name == name {
return obj, nil
obj := Map.Get(ref)
if ds, ok := obj.(*Datastore); ok && ds.Name == name {
return ds, nil
}
if p, ok := obj.(*StoragePod); ok {
ds, _ := f.findDatastore(p, name)
if ds != nil {
return ds, nil
}
case *Folder, *StoragePod:
ds, _ := f.findDatastore(obj, name)
}
if d, ok := asFolderMO(obj); ok {
ds, _ := f.findDatastore(d, name)
if ds != nil {
return ds, nil
}
@@ -126,14 +126,14 @@ func (f *FileManager) deleteDatastoreFile(req *types.DeleteDatastoreFile_Task) t
return nil
}
func (f *FileManager) DeleteDatastoreFileTask(req *types.DeleteDatastoreFile_Task) soap.HasFault {
func (f *FileManager) DeleteDatastoreFileTask(ctx *Context, req *types.DeleteDatastoreFile_Task) soap.HasFault {
task := CreateTask(f, "deleteDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) {
return nil, f.deleteDatastoreFile(req)
})
return &methods.DeleteDatastoreFile_TaskBody{
Res: &types.DeleteDatastoreFile_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}
@@ -190,14 +190,14 @@ func (f *FileManager) moveDatastoreFile(req *types.MoveDatastoreFile_Task) types
return nil
}
func (f *FileManager) MoveDatastoreFileTask(req *types.MoveDatastoreFile_Task) soap.HasFault {
func (f *FileManager) MoveDatastoreFileTask(ctx *Context, req *types.MoveDatastoreFile_Task) soap.HasFault {
task := CreateTask(f, "moveDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) {
return nil, f.moveDatastoreFile(req)
})
return &methods.MoveDatastoreFile_TaskBody{
Res: &types.MoveDatastoreFile_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}
@@ -220,7 +220,7 @@ func (f *FileManager) copyDatastoreFile(req *types.CopyDatastoreFile_Task) types
}
}
r, err := os.Open(src)
r, err := os.Open(filepath.Clean(src))
if err != nil {
return f.fault(dst, err, new(types.CannotAccessFile))
}
@@ -239,14 +239,14 @@ func (f *FileManager) copyDatastoreFile(req *types.CopyDatastoreFile_Task) types
return nil
}
func (f *FileManager) CopyDatastoreFileTask(req *types.CopyDatastoreFile_Task) soap.HasFault {
func (f *FileManager) CopyDatastoreFileTask(ctx *Context, req *types.CopyDatastoreFile_Task) soap.HasFault {
task := CreateTask(f, "copyDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) {
return nil, f.copyDatastoreFile(req)
})
return &methods.CopyDatastoreFile_TaskBody{
Res: &types.CopyDatastoreFile_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}

View File

@@ -17,12 +17,16 @@ limitations under the License.
package simulator
import (
"errors"
"fmt"
"math/rand"
"net/url"
"path"
"strings"
"time"
"github.com/google/uuid"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
@@ -34,7 +38,15 @@ type Folder struct {
mo.Folder
}
func (f *Folder) eventArgument() types.FolderEventArgument {
func asFolderMO(obj mo.Reference) (*mo.Folder, bool) {
if obj == nil {
return nil, false
}
f, ok := getManagedObject(obj).Addr().Interface().(*mo.Folder)
return f, ok
}
func folderEventArgument(f *mo.Folder) types.FolderEventArgument {
return types.FolderEventArgument{
Folder: f.Self,
EntityEventArgument: types.EntityEventArgument{Name: f.Name},
@@ -42,7 +54,7 @@ func (f *Folder) eventArgument() types.FolderEventArgument {
}
// update references when objects are added/removed from a Folder
func (f *Folder) update(o mo.Reference, u func(mo.Reference, *[]types.ManagedObjectReference, types.ManagedObjectReference)) {
func folderUpdate(ctx *Context, f *mo.Folder, o mo.Reference, u func(*Context, mo.Reference, *[]types.ManagedObjectReference, types.ManagedObjectReference)) {
ref := o.Reference()
if f.Parent == nil {
@@ -54,17 +66,20 @@ func (f *Folder) update(o mo.Reference, u func(mo.Reference, *[]types.ManagedObj
return // nothing to update
}
dc := Map.getEntityDatacenter(f)
dc := ctx.Map.getEntityDatacenter(f)
switch ref.Type {
case "Network", "DistributedVirtualSwitch", "DistributedVirtualPortgroup":
u(dc, &dc.Network, ref)
u(ctx, dc, &dc.Network, ref)
case "Datastore":
u(dc, &dc.Datastore, ref)
u(ctx, dc, &dc.Datastore, ref)
}
}
func networkSummary(n *mo.Network) *types.NetworkSummary {
func networkSummary(n *mo.Network) types.BaseNetworkSummary {
if n.Summary != nil {
return n.Summary
}
return &types.NetworkSummary{
Network: &n.Self,
Name: n.Name,
@@ -72,32 +87,38 @@ func networkSummary(n *mo.Network) *types.NetworkSummary {
}
}
func (f *Folder) putChild(o mo.Entity) {
Map.PutEntity(f, o)
func folderPutChild(ctx *Context, f *mo.Folder, o mo.Entity) {
ctx.WithLock(f, func() {
// Need to update ChildEntity before Map.Put for ContainerView updates to work properly
f.ChildEntity = append(f.ChildEntity, ctx.Map.reference(o))
ctx.Map.PutEntity(f, o)
f.ChildEntity = append(f.ChildEntity, o.Reference())
folderUpdate(ctx, f, o, ctx.Map.AddReference)
f.update(o, Map.AddReference)
switch e := o.(type) {
case *mo.Network:
e.Summary = networkSummary(e)
case *mo.OpaqueNetwork:
e.Summary = networkSummary(&e.Network)
case *DistributedVirtualPortgroup:
e.Summary = networkSummary(&e.Network)
}
ctx.WithLock(o, func() {
switch e := o.(type) {
case *mo.Network:
e.Summary = networkSummary(e)
case *mo.OpaqueNetwork:
e.Summary = networkSummary(&e.Network)
case *DistributedVirtualPortgroup:
e.Summary = networkSummary(&e.Network)
}
})
})
}
func (f *Folder) removeChild(o mo.Reference) {
Map.Remove(o.Reference())
func folderRemoveChild(ctx *Context, f *mo.Folder, o mo.Reference) {
ctx.Map.Remove(ctx, o.Reference())
RemoveReference(&f.ChildEntity, o.Reference())
ctx.WithLock(f, func() {
RemoveReference(&f.ChildEntity, o.Reference())
f.update(o, Map.RemoveReference)
folderUpdate(ctx, f, o, ctx.Map.RemoveReference)
})
}
func (f *Folder) hasChildType(kind string) bool {
func folderHasChildType(f *mo.Folder, kind string) bool {
for _, t := range f.ChildType {
if t == kind {
return true
@@ -110,14 +131,46 @@ func (f *Folder) typeNotSupported() *soap.Fault {
return Fault(fmt.Sprintf("%s supports types: %#v", f.Self, f.ChildType), &types.NotSupported{})
}
// AddOpaqueNetwork adds an OpaqueNetwork type to the inventory, with default backing to that of an nsx.LogicalSwitch.
// The vSphere API does not have a method to add this directly, so it must either be called directly or via Model.OpaqueNetwork setting.
func (f *Folder) AddOpaqueNetwork(ctx *Context, summary types.OpaqueNetworkSummary) error {
if !folderHasChildType(&f.Folder, "Network") {
return errors.New("not a network folder")
}
if summary.OpaqueNetworkId == "" {
summary.OpaqueNetworkId = uuid.New().String()
}
if summary.OpaqueNetworkType == "" {
summary.OpaqueNetworkType = "nsx.LogicalSwitch"
}
if summary.Name == "" {
summary.Name = summary.OpaqueNetworkType + "-" + summary.OpaqueNetworkId
}
net := new(mo.OpaqueNetwork)
if summary.Network == nil {
summary.Network = &net.Self
} else {
net.Self = *summary.Network
}
summary.Accessible = true
net.Network.Name = summary.Name
net.Summary = &summary
folderPutChild(ctx, &f.Folder, net)
return nil
}
type addStandaloneHost struct {
*Folder
ctx *Context
req *types.AddStandaloneHost_Task
}
func (add *addStandaloneHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
host, err := CreateStandaloneHost(add.Folder, add.req.Spec)
host, err := CreateStandaloneHost(add.ctx, add.Folder, add.req.Spec)
if err != nil {
return nil, err
}
@@ -129,12 +182,12 @@ func (add *addStandaloneHost) Run(task *Task) (types.AnyType, types.BaseMethodFa
return host.Reference(), nil
}
func (f *Folder) AddStandaloneHostTask(a *types.AddStandaloneHost_Task) soap.HasFault {
func (f *Folder) AddStandaloneHostTask(ctx *Context, a *types.AddStandaloneHost_Task) soap.HasFault {
r := &methods.AddStandaloneHost_TaskBody{}
if f.hasChildType("ComputeResource") && f.hasChildType("Folder") {
if folderHasChildType(&f.Folder, "ComputeResource") && folderHasChildType(&f.Folder, "Folder") {
r.Res = &types.AddStandaloneHost_TaskResponse{
Returnval: NewTask(&addStandaloneHost{f, a}).Run(),
Returnval: NewTask(&addStandaloneHost{f, ctx, a}).Run(ctx),
}
} else {
r.Fault_ = f.typeNotSupported()
@@ -143,16 +196,27 @@ func (f *Folder) AddStandaloneHostTask(a *types.AddStandaloneHost_Task) soap.Has
return r
}
func (f *Folder) CreateFolder(c *types.CreateFolder) soap.HasFault {
func (f *Folder) CreateFolder(ctx *Context, c *types.CreateFolder) soap.HasFault {
r := &methods.CreateFolderBody{}
if f.hasChildType("Folder") {
if folderHasChildType(&f.Folder, "Folder") {
name := escapeSpecialCharacters(c.Name)
if obj := ctx.Map.FindByName(name, f.ChildEntity); obj != nil {
r.Fault_ = Fault("", &types.DuplicateName{
Name: name,
Object: f.Self,
})
return r
}
folder := &Folder{}
folder.Name = c.Name
folder.Name = name
folder.ChildType = f.ChildType
f.putChild(folder)
folderPutChild(ctx, &f.Folder, folder)
r.Res = &types.CreateFolderResponse{
Returnval: folder.Self,
@@ -164,15 +228,31 @@ func (f *Folder) CreateFolder(c *types.CreateFolder) soap.HasFault {
return r
}
func escapeSpecialCharacters(name string) string {
name = strings.ReplaceAll(name, `%`, strings.ToLower(url.QueryEscape(`%`)))
name = strings.ReplaceAll(name, `/`, strings.ToLower(url.QueryEscape(`/`)))
name = strings.ReplaceAll(name, `\`, strings.ToLower(url.QueryEscape(`\`)))
return name
}
// StoragePod aka "Datastore Cluster"
type StoragePod struct {
mo.StoragePod
}
func (f *Folder) CreateStoragePod(c *types.CreateStoragePod) soap.HasFault {
func (f *Folder) CreateStoragePod(ctx *Context, c *types.CreateStoragePod) soap.HasFault {
r := &methods.CreateStoragePodBody{}
if f.hasChildType("StoragePod") {
if folderHasChildType(&f.Folder, "StoragePod") {
if obj := ctx.Map.FindByName(c.Name, f.ChildEntity); obj != nil {
r.Fault_ = Fault("", &types.DuplicateName{
Name: c.Name,
Object: f.Self,
})
return r
}
pod := &StoragePod{}
pod.Name = c.Name
@@ -181,7 +261,7 @@ func (f *Folder) CreateStoragePod(c *types.CreateStoragePod) soap.HasFault {
pod.PodStorageDrsEntry = new(types.PodStorageDrsEntry)
pod.PodStorageDrsEntry.StorageDrsConfig.PodConfig.Enabled = true
f.putChild(pod)
folderPutChild(ctx, &f.Folder, pod)
r.Res = &types.CreateStoragePodResponse{
Returnval: pod.Self,
@@ -193,20 +273,34 @@ func (f *Folder) CreateStoragePod(c *types.CreateStoragePod) soap.HasFault {
return r
}
func (p *StoragePod) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFault {
f := &Folder{Folder: p.Folder}
res := f.MoveIntoFolderTask(c)
p.ChildEntity = append(p.ChildEntity, f.ChildEntity...)
return res
func (p *StoragePod) MoveIntoFolderTask(ctx *Context, c *types.MoveIntoFolder_Task) soap.HasFault {
task := CreateTask(p, "moveIntoFolder", func(*Task) (types.AnyType, types.BaseMethodFault) {
f := &Folder{Folder: p.Folder}
id := f.MoveIntoFolderTask(ctx, c).(*methods.MoveIntoFolder_TaskBody).Res.Returnval
ftask := ctx.Map.Get(id).(*Task)
ftask.Wait()
if ftask.Info.Error != nil {
return nil, ftask.Info.Error.Fault
}
p.ChildEntity = append(p.ChildEntity, f.ChildEntity...)
return nil, nil
})
return &methods.MoveIntoFolder_TaskBody{
Res: &types.MoveIntoFolder_TaskResponse{
Returnval: task.Run(ctx),
},
}
}
func (f *Folder) CreateDatacenter(ctx *Context, c *types.CreateDatacenter) soap.HasFault {
r := &methods.CreateDatacenterBody{}
if f.hasChildType("Datacenter") && f.hasChildType("Folder") {
dc := NewDatacenter(f)
if folderHasChildType(&f.Folder, "Datacenter") && folderHasChildType(&f.Folder, "Folder") {
dc := NewDatacenter(ctx, &f.Folder)
dc.Name = c.Name
ctx.Map.Update(dc, []types.PropertyChange{
{Name: "name", Val: c.Name},
})
r.Res = &types.CreateDatacenterResponse{
Returnval: dc.Self,
@@ -218,7 +312,7 @@ func (f *Folder) CreateDatacenter(ctx *Context, c *types.CreateDatacenter) soap.
Datacenter: datacenterEventArgument(dc),
},
},
Parent: f.eventArgument(),
Parent: folderEventArgument(&f.Folder),
})
} else {
r.Fault_ = f.typeNotSupported()
@@ -227,11 +321,11 @@ func (f *Folder) CreateDatacenter(ctx *Context, c *types.CreateDatacenter) soap.
return r
}
func (f *Folder) CreateClusterEx(c *types.CreateClusterEx) soap.HasFault {
func (f *Folder) CreateClusterEx(ctx *Context, c *types.CreateClusterEx) soap.HasFault {
r := &methods.CreateClusterExBody{}
if f.hasChildType("ComputeResource") && f.hasChildType("Folder") {
cluster, err := CreateClusterComputeResource(f, c.Name, c.Spec)
if folderHasChildType(&f.Folder, "ComputeResource") && folderHasChildType(&f.Folder, "Folder") {
cluster, err := CreateClusterComputeResource(ctx, f, c.Name, c.Spec)
if err != nil {
r.Fault_ = Fault("", err)
return r
@@ -273,36 +367,45 @@ func hostsWithDatastore(hosts []types.ManagedObjectReference, path string) []typ
}
func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
vm, err := NewVirtualMachine(c.Folder.Self, &c.req.Config)
config := &c.req.Config
// escape special characters in vm name
if config.Name != escapeSpecialCharacters(config.Name) {
deepCopy(c.req.Config, config)
config.Name = escapeSpecialCharacters(config.Name)
}
vm, err := NewVirtualMachine(c.ctx, c.Folder.Self, &c.req.Config)
if err != nil {
c.Folder.removeChild(vm)
return nil, err
}
vm.ResourcePool = &c.req.Pool
if c.req.Host == nil {
var hosts []types.ManagedObjectReference
pool := c.ctx.Map.Get(c.req.Pool).(mo.Entity)
cr := c.ctx.Map.getEntityComputeResource(pool)
pool := Map.Get(c.req.Pool).(mo.Entity)
c.ctx.WithLock(cr, func() {
var hosts []types.ManagedObjectReference
switch cr := cr.(type) {
case *mo.ComputeResource:
hosts = cr.Host
case *ClusterComputeResource:
hosts = cr.Host
}
switch cr := Map.getEntityComputeResource(pool).(type) {
case *mo.ComputeResource:
hosts = cr.Host
case *ClusterComputeResource:
hosts = cr.Host
}
hosts = hostsWithDatastore(hosts, c.req.Config.Files.VmPathName)
host := hosts[rand.Intn(len(hosts))]
vm.Runtime.Host = &host
hosts = hostsWithDatastore(hosts, c.req.Config.Files.VmPathName)
host := hosts[rand.Intn(len(hosts))]
vm.Runtime.Host = &host
})
} else {
vm.Runtime.Host = c.req.Host
}
vm.Guest = &types.GuestInfo{
ToolsStatus: types.VirtualMachineToolsStatusToolsNotInstalled,
ToolsVersion: "0",
ToolsStatus: types.VirtualMachineToolsStatusToolsNotInstalled,
ToolsVersion: "0",
ToolsRunningStatus: string(types.VirtualMachineToolsRunningStatusGuestToolsNotRunning),
}
vm.Summary.Guest = &types.VirtualMachineGuestSummary{
@@ -311,30 +414,30 @@ func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
vm.Summary.Config.VmPathName = vm.Config.Files.VmPathName
vm.Summary.Runtime.Host = vm.Runtime.Host
err = vm.create(&c.req.Config, c.register)
err = vm.create(c.ctx, &c.req.Config, c.register)
if err != nil {
c.Folder.removeChild(vm)
folderRemoveChild(c.ctx, &c.Folder.Folder, vm)
return nil, err
}
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
Map.AppendReference(host, &host.Vm, vm.Self)
host := c.ctx.Map.Get(*vm.Runtime.Host).(*HostSystem)
c.ctx.Map.AppendReference(c.ctx, host, &host.Vm, vm.Self)
vm.EnvironmentBrowser = *hostParent(&host.HostSystem).EnvironmentBrowser
for i := range vm.Datastore {
ds := Map.Get(vm.Datastore[i]).(*Datastore)
Map.AppendReference(ds, &ds.Vm, vm.Self)
ds := c.ctx.Map.Get(vm.Datastore[i]).(*Datastore)
c.ctx.Map.AppendReference(c.ctx, ds, &ds.Vm, vm.Self)
}
pool := Map.Get(*vm.ResourcePool)
pool := c.ctx.Map.Get(*vm.ResourcePool)
// This can be an internal call from VirtualApp.CreateChildVMTask, where pool is already locked.
c.ctx.WithLock(pool, func() {
switch rp := pool.(type) {
case *ResourcePool:
rp.Vm = append(rp.Vm, vm.Self)
case *VirtualApp:
if rp, ok := asResourcePoolMO(pool); ok {
rp.Vm = append(rp.Vm, vm.Self)
}
if vapp, ok := pool.(*VirtualApp); ok {
vapp.Vm = append(vapp.Vm, vm.Self)
}
})
event := vm.event()
@@ -358,13 +461,17 @@ func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
vm.RefreshStorageInfo(c.ctx, nil)
c.ctx.Map.Update(vm, []types.PropertyChange{
{Name: "name", Val: c.req.Config.Name},
})
return vm.Reference(), nil
}
func (f *Folder) CreateVMTask(ctx *Context, c *types.CreateVM_Task) soap.HasFault {
return &methods.CreateVM_TaskBody{
Res: &types.CreateVM_TaskResponse{
Returnval: NewTask(&createVM{f, ctx, c, false}).Run(),
Returnval: NewTask(&createVM{f, ctx, c, false}).Run(ctx),
},
}
}
@@ -387,7 +494,7 @@ func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
return nil, &types.InvalidArgument{InvalidProperty: "pool"}
}
pool = hostParent(&Map.Get(*host).(*HostSystem).HostSystem).ResourcePool
pool = hostParent(&c.ctx.Map.Get(*host).(*HostSystem).HostSystem).ResourcePool
} else {
if pool == nil {
return nil, &types.InvalidArgument{InvalidProperty: "pool"}
@@ -398,11 +505,11 @@ func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
return nil, &types.InvalidArgument{InvalidProperty: "path"}
}
s := Map.SearchIndex()
s := c.ctx.Map.SearchIndex()
r := s.FindByDatastorePath(&types.FindByDatastorePath{
This: s.Reference(),
Path: c.req.Path,
Datacenter: Map.getEntityDatacenter(c.Folder).Reference(),
Datacenter: c.ctx.Map.getEntityDatacenter(c.Folder).Reference(),
})
if ref := r.(*methods.FindByDatastorePathBody).Res.Returnval; ref != nil {
@@ -435,7 +542,7 @@ func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
},
})
create.Run()
create.RunBlocking(c.ctx)
if create.Info.Error != nil {
return nil, create.Info.Error.Fault
@@ -445,28 +552,26 @@ func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
}
func (f *Folder) RegisterVMTask(ctx *Context, c *types.RegisterVM_Task) soap.HasFault {
ctx.Caller = &f.Self
return &methods.RegisterVM_TaskBody{
Res: &types.RegisterVM_TaskResponse{
Returnval: NewTask(&registerVM{f, ctx, c}).Run(),
Returnval: NewTask(&registerVM{f, ctx, c}).Run(ctx),
},
}
}
func (f *Folder) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFault {
func (f *Folder) MoveIntoFolderTask(ctx *Context, c *types.MoveIntoFolder_Task) soap.HasFault {
task := CreateTask(f, "moveIntoFolder", func(t *Task) (types.AnyType, types.BaseMethodFault) {
for _, ref := range c.List {
obj := Map.Get(ref).(mo.Entity)
obj := ctx.Map.Get(ref).(mo.Entity)
parent, ok := Map.Get(*(obj.Entity()).Parent).(*Folder)
parent, ok := ctx.Map.Get(*(obj.Entity()).Parent).(*Folder)
if !ok || !f.hasChildType(ref.Type) {
if !ok || !folderHasChildType(&f.Folder, ref.Type) {
return nil, &types.NotSupported{}
}
parent.removeChild(ref)
f.putChild(obj)
folderRemoveChild(ctx, &parent.Folder, ref)
folderPutChild(ctx, &f.Folder, obj)
}
return nil, nil
@@ -474,25 +579,25 @@ func (f *Folder) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFault
return &methods.MoveIntoFolder_TaskBody{
Res: &types.MoveIntoFolder_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}
func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault {
func (f *Folder) CreateDVSTask(ctx *Context, req *types.CreateDVS_Task) soap.HasFault {
task := CreateTask(f, "createDVS", func(t *Task) (types.AnyType, types.BaseMethodFault) {
spec := req.Spec.ConfigSpec.GetDVSConfigSpec()
dvs := &DistributedVirtualSwitch{}
dvs.Name = spec.Name
dvs.Entity().Name = dvs.Name
if Map.FindByName(dvs.Name, f.ChildEntity) != nil {
if ctx.Map.FindByName(dvs.Name, f.ChildEntity) != nil {
return nil, &types.InvalidArgument{InvalidProperty: "name"}
}
dvs.Uuid = uuid.New().String()
dvs.Uuid = newUUID(dvs.Name)
f.putChild(dvs)
folderPutChild(ctx, &f.Folder, dvs)
dvs.Summary = types.DVSSummary{
Name: dvs.Name,
@@ -530,7 +635,7 @@ func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault {
dvs.Config = configInfo
if dvs.Summary.ProductInfo == nil {
product := Map.content().About
product := ctx.Map.content().About
dvs.Summary.ProductInfo = &types.DistributedVirtualSwitchProductSpec{
Name: "DVS",
Vendor: product.Vendor,
@@ -540,13 +645,29 @@ func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault {
}
}
dvs.AddDVPortgroupTask(&types.AddDVPortgroup_Task{
dvs.AddDVPortgroupTask(ctx, &types.AddDVPortgroup_Task{
Spec: []types.DVPortgroupConfigSpec{{
Name: dvs.Name + "-DVUplinks" + strings.TrimPrefix(dvs.Self.Value, "dvs"),
Name: dvs.Name + "-DVUplinks" + strings.TrimPrefix(dvs.Self.Value, "dvs"),
Type: string(types.DistributedVirtualPortgroupPortgroupTypeEarlyBinding),
NumPorts: 1,
DefaultPortConfig: &types.VMwareDVSPortSetting{
Vlan: &types.VmwareDistributedVirtualSwitchTrunkVlanSpec{
VlanId: []types.NumericRange{{Start: 0, End: 4094}},
},
UplinkTeamingPolicy: &types.VmwareUplinkPortTeamingPolicy{
Policy: &types.StringPolicy{
Value: "loadbalance_srcid",
},
ReversePolicy: &types.BoolPolicy{
Value: types.NewBool(true),
},
NotifySwitches: &types.BoolPolicy{
Value: types.NewBool(true),
},
RollingOrder: &types.BoolPolicy{
Value: types.NewBool(true),
},
},
},
}},
})
@@ -556,16 +677,16 @@ func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault {
return &methods.CreateDVS_TaskBody{
Res: &types.CreateDVS_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}
func (f *Folder) RenameTask(r *types.Rename_Task) soap.HasFault {
return RenameTask(f, r)
func (f *Folder) RenameTask(ctx *Context, r *types.Rename_Task) soap.HasFault {
return RenameTask(ctx, f, r)
}
func (f *Folder) DestroyTask(req *types.Destroy_Task) soap.HasFault {
func (f *Folder) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
type destroyer interface {
mo.Reference
DestroyTask(*types.Destroy_Task) soap.HasFault
@@ -574,18 +695,19 @@ func (f *Folder) DestroyTask(req *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)
obj, ok := ctx.Map.Get(c).(destroyer)
if !ok {
continue
}
var fault types.BaseMethodFault
Map.WithLock(obj, func() {
ctx.WithLock(obj, func() {
id := obj.DestroyTask(&types.Destroy_Task{
This: c,
}).(*methods.Destroy_TaskBody).Res.Returnval
t := Map.Get(id).(*Task)
t := ctx.Map.Get(id).(*Task)
t.Wait()
if t.Info.Error != nil {
fault = t.Info.Error.Fault // For example, can't destroy a powered on VM
}
@@ -596,13 +718,159 @@ func (f *Folder) DestroyTask(req *types.Destroy_Task) soap.HasFault {
}
// Remove the folder itself
Map.Get(*f.Parent).(*Folder).removeChild(f.Self)
folderRemoveChild(ctx, &ctx.Map.Get(*f.Parent).(*Folder).Folder, f.Self)
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(),
Returnval: task.Run(ctx),
},
}
}
func (f *Folder) PlaceVmsXCluster(ctx *Context, req *types.PlaceVmsXCluster) soap.HasFault {
body := new(methods.PlaceVmsXClusterBody)
// Reject the request if it is against any folder other than the root folder.
if req.This != ctx.Map.content().RootFolder {
body.Fault_ = Fault("", new(types.InvalidRequest))
return body
}
pools := req.PlacementSpec.ResourcePools
specs := req.PlacementSpec.VmPlacementSpecs
if len(pools) == 0 {
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "resourcePools"})
return body
}
// Do not allow duplicate clusters.
clusters := map[mo.Reference]struct{}{}
for _, obj := range pools {
o := ctx.Map.Get(obj)
pool, ok := o.(*ResourcePool)
if !ok {
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "resourcePool"})
return body
}
if _, exists := clusters[pool.Owner]; exists {
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "clusters"})
return body
}
clusters[pool.Owner] = struct{}{}
}
// MVP: Only a single VM is supported.
if len(specs) != 1 {
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "vmPlacementSpecs"})
return body
}
for _, spec := range specs {
if spec.ConfigSpec.Name == "" {
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "configSpec.name"})
return body
}
}
body.Res = new(types.PlaceVmsXClusterResponse)
hostRequired := req.PlacementSpec.HostRecommRequired != nil && *req.PlacementSpec.HostRecommRequired
datastoreRequired := req.PlacementSpec.DatastoreRecommRequired != nil && *req.PlacementSpec.DatastoreRecommRequired
for _, spec := range specs {
pool := ctx.Map.Get(pools[rand.Intn(len(pools))]).(*ResourcePool)
cluster := ctx.Map.Get(pool.Owner).(*ClusterComputeResource)
if len(cluster.Host) == 0 {
faults := types.PlaceVmsXClusterResultPlacementFaults{
VmName: spec.ConfigSpec.Name,
ResourcePool: pool.Self,
Faults: []types.LocalizedMethodFault{
{
Fault: &types.GenericDrsFault{},
},
},
}
body.Res.Returnval.Faults = append(body.Res.Returnval.Faults, faults)
} else {
var configSpec *types.VirtualMachineConfigSpec
res := types.ClusterRecommendation{
Key: "1",
Type: "V1",
Time: time.Now(),
Rating: 1,
Reason: string(types.RecommendationReasonCodeXClusterPlacement),
ReasonText: string(types.RecommendationReasonCodeXClusterPlacement),
Target: &cluster.Self,
}
placementAction := types.ClusterClusterInitialPlacementAction{
Pool: pool.Self,
}
if hostRequired {
randomHost := cluster.Host[rand.Intn(len(cluster.Host))]
placementAction.TargetHost = &randomHost
}
if datastoreRequired {
configSpec = &spec.ConfigSpec
// TODO: This is just an initial implementation aimed at returning some data but it is not
// necessarily fully consistent, like we should ensure the host, if also required, has the
// datastore mounted.
ds := ctx.Map.Get(cluster.Datastore[rand.Intn(len(cluster.Datastore))]).(*Datastore)
if configSpec.Files == nil {
configSpec.Files = new(types.VirtualMachineFileInfo)
}
configSpec.Files.VmPathName = fmt.Sprintf("[%[1]s] %[2]s/%[2]s.vmx", ds.Name, spec.ConfigSpec.Name)
for _, change := range configSpec.DeviceChange {
dspec := change.GetVirtualDeviceConfigSpec()
if dspec.FileOperation != types.VirtualDeviceConfigSpecFileOperationCreate {
continue
}
switch dspec.Operation {
case types.VirtualDeviceConfigSpecOperationAdd:
device := dspec.Device
d := device.GetVirtualDevice()
switch device.(type) {
case *types.VirtualDisk:
switch b := d.Backing.(type) {
case types.BaseVirtualDeviceFileBackingInfo:
info := b.GetVirtualDeviceFileBackingInfo()
info.Datastore = types.NewReference(ds.Reference())
var dsPath object.DatastorePath
if dsPath.FromString(info.FileName) {
dsPath.Datastore = ds.Name
info.FileName = dsPath.String()
}
}
}
}
}
placementAction.ConfigSpec = configSpec
}
res.Action = append(res.Action, &placementAction)
body.Res.Returnval.PlacementInfos = append(body.Res.Returnval.PlacementInfos,
types.PlaceVmsXClusterResultPlacementInfo{
VmName: spec.ConfigSpec.Name,
Recommendation: res,
},
)
}
}
return body
}

Some files were not shown because too many files have changed in this diff Show More