godep: update vmware/govmomi

This commit is contained in:
Doug MacEachern 2018-01-30 12:03:39 -08:00
parent f821a54d39
commit 5c27b98ce0
46 changed files with 2205 additions and 370 deletions

88
Godeps/Godeps.json generated
View File

@ -2644,113 +2644,113 @@
},
{
"ImportPath": "github.com/vmware/govmomi",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/find",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/list",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/nfc",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/object",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/pbm",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/pbm/methods",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/pbm/types",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/property",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/session",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/simulator",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/simulator/esx",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/simulator/vpx",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/task",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/debug",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/methods",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/mo",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/progress",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/soap",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/types",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/xml",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/photon-controller-go-sdk/SSPI",

View File

@ -18,7 +18,7 @@ install:
go install -v github.com/vmware/govmomi/vcsim
go-test:
go test -v $(TEST_OPTS) ./...
go test -race -v $(TEST_OPTS) ./...
govc-test: install
(cd govc/test && ./vendor/github.com/sstephenson/bats/libexec/bats -t .)

View File

@ -625,6 +625,15 @@ func (f *Finder) ClusterComputeResourceList(ctx context.Context, path string) ([
return ccrs, nil
}
func (f *Finder) DefaultClusterComputeResource(ctx context.Context) (*object.ClusterComputeResource, error) {
cr, err := f.ClusterComputeResource(ctx, "*")
if err != nil {
return nil, toDefaultError(err)
}
return cr, nil
}
func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*object.ClusterComputeResource, error) {
ccrs, err := f.ClusterComputeResourceList(ctx, path)
if err != nil {
@ -638,6 +647,18 @@ func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*obje
return ccrs[0], nil
}
func (f *Finder) ClusterComputeResourceOrDefault(ctx context.Context, path string) (*object.ClusterComputeResource, error) {
if path != "" {
cr, err := f.ClusterComputeResource(ctx, path)
if err != nil {
return nil, err
}
return cr, nil
}
return f.DefaultClusterComputeResource(ctx)
}
func (f *Finder) HostSystemList(ctx context.Context, path string) ([]*object.HostSystem, error) {
s := &spec{
Relative: f.hostFolder,

View File

@ -224,7 +224,7 @@ func (l *Lease) Upload(ctx context.Context, item FileItem, f io.Reader, opts soa
opts.Type = "application/x-vnd.vmware-streamVmdk"
}
return l.c.Upload(f, item.URL, &opts)
return l.c.Upload(ctx, f, item.URL, &opts)
}
func (l *Lease) DownloadFile(ctx context.Context, file string, item FileItem, opts soap.Download) error {
@ -234,5 +234,5 @@ func (l *Lease) DownloadFile(ctx context.Context, file string, item FileItem, op
opts.Progress = progress.Tee(item, opts.Progress)
}
return l.c.DownloadFile(file, item.URL, &opts)
return l.c.DownloadFile(ctx, file, item.URL, &opts)
}

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"
)
@ -34,19 +35,15 @@ func NewClusterComputeResource(c *vim25.Client, ref types.ManagedObjectReference
}
}
func (c ClusterComputeResource) ReconfigureCluster(ctx context.Context, spec types.ClusterConfigSpec) (*Task, error) {
req := types.ReconfigureCluster_Task{
This: c.Reference(),
Spec: spec,
Modify: true,
}
func (c ClusterComputeResource) Configuration(ctx context.Context) (*types.ClusterConfigInfoEx, error) {
var obj mo.ClusterComputeResource
res, err := methods.ReconfigureCluster_Task(ctx, c.c, &req)
err := c.Properties(ctx, c.Reference(), []string{"configurationEx"}, &obj)
if err != nil {
return nil, err
}
return NewTask(c.c, res.Returnval), nil
return obj.ConfigurationEx.(*types.ClusterConfigInfoEx), nil
}
func (c ClusterComputeResource) AddHost(ctx context.Context, spec types.HostConnectSpec, asConnected bool, license *string, resourcePool *types.ManagedObjectReference) (*Task, error) {
@ -71,16 +68,3 @@ func (c ClusterComputeResource) AddHost(ctx context.Context, spec types.HostConn
return NewTask(c.c, res.Returnval), nil
}
func (c ClusterComputeResource) Destroy(ctx context.Context) (*Task, error) {
req := types.Destroy_Task{
This: c.Reference(),
}
res, err := methods.Destroy_Task(ctx, c.c, &req)
if err != nil {
return nil, err
}
return NewTask(c.c, res.Returnval), nil
}

View File

@ -109,16 +109,3 @@ func (c ComputeResource) Reconfigure(ctx context.Context, spec types.BaseCompute
return NewTask(c.c, res.Returnval), nil
}
func (c ComputeResource) Destroy(ctx context.Context) (*Task, error) {
req := types.Destroy_Task{
This: c.Reference(),
}
res, err := methods.Destroy_Task(ctx, c.c, &req)
if err != nil {
return nil, err
}
return NewTask(c.c, res.Returnval), nil
}

View File

@ -284,7 +284,7 @@ func (d Datastore) Upload(ctx context.Context, f io.Reader, path string, param *
if err != nil {
return err
}
return d.Client().Upload(f, u, p)
return d.Client().Upload(ctx, f, u, p)
}
// UploadFile via soap.Upload with an http service ticket
@ -293,7 +293,7 @@ func (d Datastore) UploadFile(ctx context.Context, file string, path string, par
if err != nil {
return err
}
return d.Client().UploadFile(file, u, p)
return d.Client().UploadFile(ctx, file, u, p)
}
// Download via soap.Download with an http service ticket
@ -302,7 +302,7 @@ func (d Datastore) Download(ctx context.Context, path string, param *soap.Downlo
if err != nil {
return nil, 0, err
}
return d.Client().Download(u, p)
return d.Client().Download(ctx, u, p)
}
// DownloadFile via soap.Download with an http service ticket
@ -311,7 +311,7 @@ func (d Datastore) DownloadFile(ctx context.Context, path string, file string, p
if err != nil {
return err
}
return d.Client().DownloadFile(file, u, p)
return d.Client().DownloadFile(ctx, file, u, p)
}
// AttachedHosts returns hosts that have this Datastore attached, accessible and writable.
@ -406,12 +406,9 @@ func (d Datastore) Stat(ctx context.Context, file string) (types.BaseFileInfo, e
info, err := task.WaitForResult(ctx, nil)
if err != nil {
if info == nil || info.Error != nil {
_, ok := info.Error.Fault.(*types.FileNotFound)
if ok {
// FileNotFound means the base path doesn't exist.
return nil, DatastoreNoSuchDirectoryError{"stat", dsPath}
}
if types.IsFileNotFound(err) {
// FileNotFound means the base path doesn't exist.
return nil, DatastoreNoSuchDirectoryError{"stat", dsPath}
}
return nil, err

View File

@ -172,7 +172,7 @@ func (f *DatastoreFile) Stat() (os.FileInfo, error) {
return nil, err
}
res, err := f.d.Client().DownloadRequest(u, p)
res, err := f.d.Client().DownloadRequest(f.ctx, u, p)
if err != nil {
return nil, err
}
@ -202,7 +202,7 @@ func (f *DatastoreFile) get() (io.Reader, error) {
}
}
res, err := f.d.Client().DownloadRequest(u, p)
res, err := f.d.Client().DownloadRequest(f.ctx, u, p)
if err != nil {
return nil, err
}

View File

@ -97,6 +97,15 @@ func (s HostStorageSystem) Refresh(ctx context.Context) error {
return err
}
func (s HostStorageSystem) RescanVmfs(ctx context.Context) error {
req := types.RescanVmfs{
This: s.Reference(),
}
_, err := methods.RescanVmfs(ctx, s.c, &req)
return err
}
func (s HostStorageSystem) MarkAsSsd(ctx context.Context, uuid string) (*Task, error) {
req := types.MarkAsSsd_Task{
This: s.Reference(),
@ -152,3 +161,14 @@ func (s HostStorageSystem) MarkAsNonLocal(ctx context.Context, uuid string) (*Ta
return NewTask(s.c, res.Returnval), nil
}
func (s HostStorageSystem) AttachScsiLun(ctx context.Context, uuid string) error {
req := types.AttachScsiLun{
This: s.Reference(),
LunUuid: uuid,
}
_, err := methods.AttachScsiLun(ctx, s.c, &req)
return err
}

View File

@ -145,6 +145,47 @@ func (m VirtualDiskManager) DeleteVirtualDisk(ctx context.Context, name string,
return NewTask(m.c, res.Returnval), nil
}
// InflateVirtualDisk inflates a virtual disk.
func (m VirtualDiskManager) InflateVirtualDisk(ctx context.Context, name string, dc *Datacenter) (*Task, error) {
req := types.InflateVirtualDisk_Task{
This: m.Reference(),
Name: name,
}
if dc != nil {
ref := dc.Reference()
req.Datacenter = &ref
}
res, err := methods.InflateVirtualDisk_Task(ctx, m.c, &req)
if err != nil {
return nil, err
}
return NewTask(m.c, res.Returnval), nil
}
// ShrinkVirtualDisk shrinks a virtual disk.
func (m VirtualDiskManager) ShrinkVirtualDisk(ctx context.Context, name string, dc *Datacenter, copy *bool) (*Task, error) {
req := types.ShrinkVirtualDisk_Task{
This: m.Reference(),
Name: name,
Copy: copy,
}
if dc != nil {
ref := dc.Reference()
req.Datacenter = &ref
}
res, err := methods.ShrinkVirtualDisk_Task(ctx, m.c, &req)
if err != nil {
return nil, err
}
return NewTask(m.c, res.Returnval), nil
}
// Queries virtual disk uuid
func (m VirtualDiskManager) QueryVirtualDiskUuid(ctx context.Context, name string, dc *Datacenter) (string, error) {
req := types.QueryVirtualDiskUuid{

View File

@ -95,3 +95,67 @@ func (m VirtualDiskManager) QueryVirtualDiskInfo(ctx context.Context, name strin
return info.Result.(arrayOfVirtualDiskInfo).VirtualDiskInfo, nil
}
type createChildDiskTaskRequest struct {
This types.ManagedObjectReference `xml:"_this"`
ChildName string `xml:"childName"`
ChildDatacenter *types.ManagedObjectReference `xml:"childDatacenter,omitempty"`
ParentName string `xml:"parentName"`
ParentDatacenter *types.ManagedObjectReference `xml:"parentDatacenter,omitempty"`
IsLinkedClone bool `xml:"isLinkedClone"`
}
type createChildDiskTaskResponse struct {
Returnval types.ManagedObjectReference `xml:"returnval"`
}
type createChildDiskTaskBody struct {
Req *createChildDiskTaskRequest `xml:"urn:internalvim25 CreateChildDisk_Task,omitempty"`
Res *createChildDiskTaskResponse `xml:"urn:vim25 CreateChildDisk_TaskResponse,omitempty"`
InternalRes *createChildDiskTaskResponse `xml:"urn:internalvim25 CreateChildDisk_TaskResponse,omitempty"`
Err *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *createChildDiskTaskBody) Fault() *soap.Fault { return b.Err }
func createChildDiskTask(ctx context.Context, r soap.RoundTripper, req *createChildDiskTaskRequest) (*createChildDiskTaskResponse, error) {
var reqBody, resBody createChildDiskTaskBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
if resBody.Res != nil {
return resBody.Res, nil // vim-version <= 6.5
}
return resBody.InternalRes, nil // vim-version >= 6.7
}
func (m VirtualDiskManager) CreateChildDisk(ctx context.Context, parent string, pdc *Datacenter, name string, dc *Datacenter, linked bool) (*Task, error) {
req := createChildDiskTaskRequest{
This: m.Reference(),
ChildName: name,
ParentName: parent,
IsLinkedClone: linked,
}
if dc != nil {
ref := dc.Reference()
req.ChildDatacenter = &ref
}
if pdc != nil {
ref := pdc.Reference()
req.ParentDatacenter = &ref
}
res, err := createChildDiskTask(ctx, m.Client(), &req)
if err != nil {
return nil, err
}
return NewTask(m.Client(), res.Returnval), nil
}

View File

@ -121,6 +121,10 @@ func (p *Collector) RetrieveProperties(ctx context.Context, req types.RetrievePr
// of the specified managed objects, with the relevant properties filled in. If
// the properties slice is nil, all properties are loaded.
func (p *Collector) Retrieve(ctx context.Context, objs []types.ManagedObjectReference, ps []string, dst interface{}) error {
if len(objs) == 0 {
return errors.New("object references is empty")
}
var propSpec *types.PropertySpec
var objectSet []types.ObjectSpec

View File

@ -11,12 +11,14 @@ go_library(
"doc.go",
"dvs.go",
"entity.go",
"event_manager.go",
"file_manager.go",
"folder.go",
"guest_id.go",
"host_datastore_browser.go",
"host_datastore_system.go",
"host_firewall_system.go",
"host_local_account_manager.go",
"host_network_system.go",
"host_system.go",
"ip_pool_manager.go",

View File

@ -39,7 +39,8 @@ type AuthorizationManager struct {
func NewAuthorizationManager(ref types.ManagedObjectReference) object.Reference {
m := &AuthorizationManager{}
m.Self = ref
m.RoleList = esx.RoleList
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)

View File

@ -17,6 +17,9 @@ limitations under the License.
package simulator
import (
"sync/atomic"
"github.com/google/uuid"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
@ -26,6 +29,8 @@ import (
type ClusterComputeResource struct {
mo.ClusterComputeResource
ruleKey int32
}
type addHost struct {
@ -44,18 +49,18 @@ func (add *addHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
host := NewHostSystem(esx.HostSystem)
host.Summary.Config.Name = spec.HostName
host.Name = host.Summary.Config.Name
host.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected
if add.req.AsConnected {
host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
} else {
host.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected
}
cr := add.ClusterComputeResource
Map.PutEntity(cr, Map.NewEntity(host))
host.Summary.Host = &host.Self
cr.Host = append(cr.Host, host.Reference())
if add.req.AsConnected {
host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
}
addComputeResource(add.ClusterComputeResource.Summary.GetComputeResourceSummary(), host)
addComputeResource(cr.Summary.GetComputeResourceSummary(), host)
return host.Reference(), nil
}
@ -68,6 +73,224 @@ func (c *ClusterComputeResource) AddHostTask(add *types.AddHost_Task) soap.HasFa
}
}
func (c *ClusterComputeResource) updateRules(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.RulesSpec {
var i int
exists := false
match := func(info types.BaseClusterRuleInfo) bool {
return info.GetClusterRuleInfo().Name == spec.Info.GetClusterRuleInfo().Name
}
if spec.Operation == types.ArrayUpdateOperationRemove {
match = func(rule types.BaseClusterRuleInfo) bool {
return rule.GetClusterRuleInfo().Key == spec.ArrayUpdateSpec.RemoveKey.(int32)
}
}
for i = range cfg.Rule {
if match(cfg.Rule[i].GetClusterRuleInfo()) {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
info := spec.Info.GetClusterRuleInfo()
info.Key = atomic.AddInt32(&c.ruleKey, 1)
info.RuleUuid = uuid.New().String()
cfg.Rule = append(cfg.Rule, spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
cfg.Rule[i] = spec.Info
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.Rule = append(cfg.Rule[:i], cfg.Rule[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) updateGroups(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.GroupSpec {
var i int
exists := false
match := func(info types.BaseClusterGroupInfo) bool {
return info.GetClusterGroupInfo().Name == spec.Info.GetClusterGroupInfo().Name
}
if spec.Operation == types.ArrayUpdateOperationRemove {
match = func(info types.BaseClusterGroupInfo) bool {
return info.GetClusterGroupInfo().Name == spec.ArrayUpdateSpec.RemoveKey.(string)
}
}
for i = range cfg.Group {
if match(cfg.Group[i].GetClusterGroupInfo()) {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
cfg.Group = append(cfg.Group, spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
cfg.Group[i] = spec.Info
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.Group = append(cfg.Group[:i], cfg.Group[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) updateOverridesDAS(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.DasVmConfigSpec {
var i int
var key types.ManagedObjectReference
exists := false
if spec.Operation == types.ArrayUpdateOperationRemove {
key = spec.RemoveKey.(types.ManagedObjectReference)
} else {
key = spec.Info.Key
}
for i = range cfg.DasVmConfig {
if cfg.DasVmConfig[i].Key == key {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
cfg.DasVmConfig = append(cfg.DasVmConfig, *spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
src := spec.Info.DasSettings
if src == nil {
return new(types.InvalidArgument)
}
dst := cfg.DasVmConfig[i].DasSettings
if src.RestartPriority != "" {
dst.RestartPriority = src.RestartPriority
}
if src.RestartPriorityTimeout != 0 {
dst.RestartPriorityTimeout = src.RestartPriorityTimeout
}
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.DasVmConfig = append(cfg.DasVmConfig[:i], cfg.DasVmConfig[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) updateOverridesDRS(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.DrsVmConfigSpec {
var i int
var key types.ManagedObjectReference
exists := false
if spec.Operation == types.ArrayUpdateOperationRemove {
key = spec.RemoveKey.(types.ManagedObjectReference)
} else {
key = spec.Info.Key
}
for i = range cfg.DrsVmConfig {
if cfg.DrsVmConfig[i].Key == key {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
cfg.DrsVmConfig = append(cfg.DrsVmConfig, *spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
if spec.Info.Enabled != nil {
cfg.DrsVmConfig[i].Enabled = spec.Info.Enabled
}
if spec.Info.Behavior != "" {
cfg.DrsVmConfig[i].Behavior = spec.Info.Behavior
}
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.DrsVmConfig = append(cfg.DrsVmConfig[:i], cfg.DrsVmConfig[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) ReconfigureComputeResourceTask(req *types.ReconfigureComputeResource_Task) soap.HasFault {
task := CreateTask(c, "reconfigureCluster", func(*Task) (types.AnyType, types.BaseMethodFault) {
spec, ok := req.Spec.(*types.ClusterConfigSpecEx)
if !ok {
return nil, new(types.InvalidArgument)
}
updates := []func(*types.ClusterConfigInfoEx, *types.ClusterConfigSpecEx) types.BaseMethodFault{
c.updateRules,
c.updateGroups,
c.updateOverridesDAS,
c.updateOverridesDRS,
}
for _, update := range updates {
if err := update(c.ConfigurationEx.(*types.ClusterConfigInfoEx), spec); err != nil {
return nil, err
}
}
return nil, nil
})
return &methods.ReconfigureComputeResource_TaskBody{
Res: &types.ReconfigureComputeResource_TaskResponse{
Returnval: task.Run(),
},
}
}
func CreateClusterComputeResource(f *Folder, name string, spec types.ClusterConfigSpecEx) (*ClusterComputeResource, types.BaseMethodFault) {
if e := Map.FindByName(name, f.ChildEntity); e != nil {
return nil, &types.DuplicateName{
@ -85,6 +308,7 @@ func CreateClusterComputeResource(f *Folder, name string, spec types.ClusterConf
config := &types.ClusterConfigInfoEx{}
cluster.ConfigurationEx = config
config.VmSwapPlacement = string(types.VirtualMachineConfigInfoSwapPlacementTypeVmDirectory)
config.DrsConfig.Enabled = types.NewBool(true)
pool := NewResourcePool()

View File

@ -101,9 +101,11 @@ func (c *CustomFieldsManager) SetField(req *types.SetField) soap.HasFault {
body := &methods.SetFieldBody{}
entity := Map.Get(req.Entity).(mo.Entity).Entity()
entity.CustomValue = append(entity.CustomValue, &types.CustomFieldStringValue{
CustomFieldValue: types.CustomFieldValue{Key: req.Key},
Value: req.Value,
Map.WithLock(entity, func() {
entity.CustomValue = append(entity.CustomValue, &types.CustomFieldStringValue{
CustomFieldValue: types.CustomFieldValue{Key: req.Key},
Value: req.Value,
})
})
body.Res = &types.SetFieldResponse{}

View File

@ -74,3 +74,14 @@ func createDatacenterFolders(dc *mo.Datacenter, isVC bool) {
net.putChild(network)
}
}
func datacenterEventArgument(obj mo.Entity) *types.DatacenterEventArgument {
dc, ok := obj.(*mo.Datacenter)
if !ok {
dc = Map.getEntityDatacenter(obj)
}
return &types.DatacenterEventArgument{
Datacenter: dc.Self,
EntityEventArgument: types.EntityEventArgument{Name: dc.Name},
}
}

View File

@ -67,9 +67,10 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Ta
s.Summary.PortgroupName = append(s.Summary.PortgroupName, pg.Name)
for _, h := range s.Summary.HostMember {
pg.Host = AddReference(h, pg.Host)
pg.Host = append(pg.Host, h)
host := Map.Get(h).(*HostSystem)
host.Network = append(host.Network, pg.Reference())
Map.AppendReference(host, &host.Network, pg.Reference())
}
}
@ -101,13 +102,13 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
return nil, &types.AlreadyExists{Name: host.Name}
}
host.Network = append(host.Network, s.Self)
host.Network = append(host.Network, s.Portgroup...)
Map.AppendReference(host, &host.Network, s.Self)
Map.AppendReference(host, &host.Network, s.Portgroup...)
s.Summary.HostMember = append(s.Summary.HostMember, member.Host)
for _, ref := range s.Portgroup {
pg := Map.Get(ref).(*DistributedVirtualPortgroup)
pg.Host = AddReference(member.Host, pg.Host)
Map.AddReference(pg, &pg.Host, member.Host)
}
case types.ConfigSpecOperationRemove:
if pg := FindReference(host.Network, s.Portgroup...); pg != nil {
@ -117,8 +118,8 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
}
}
host.Network = RemoveReference(s.Self, host.Network)
s.Summary.HostMember = RemoveReference(s.Self, s.Summary.HostMember)
Map.RemoveReference(host, &host.Network, s.Self)
RemoveReference(&s.Summary.HostMember, s.Self)
case types.ConfigSpecOperationEdit:
return nil, &types.NotSupported{}
}

View File

@ -6,6 +6,7 @@ go_library(
"authorization_manager.go",
"datacenter.go",
"doc.go",
"event_manager.go",
"host_config_info.go",
"host_firewall_system.go",
"host_hardware_info.go",

View File

@ -0,0 +1,236 @@
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package esx
import "github.com/vmware/govmomi/vim25/types"
// EventInfo is the default template for the EventManager description.eventInfo property.
// Capture method:
// govc object.collect -s -dump EventManager:ha-eventmgr description.eventInfo
// The captured list has been manually pruned and FullFormat fields changed to use Go's template variable syntax.
var EventInfo = []types.EventDescriptionEventDetail{
{
Key: "UserLoginSessionEvent",
Description: "User login",
Category: "info",
FullFormat: "User {{.UserName}}@{{.IpAddress}} logged in as {{.UserAgent}}",
},
{
Key: "UserLogoutSessionEvent",
Description: "User logout",
Category: "info",
FullFormat: "User {{.UserName}}@{{.IpAddress}} logged out (login time: {{.LoginTime}}, number of API invocations: {{.CallCount}}, user agent: {{.UserAgent}})",
},
{
Key: "DatacenterCreatedEvent",
Description: "Datacenter created",
Category: "info",
FullFormat: "Created datacenter {{.Datacenter.Name}} in folder {{.Parent.Name}}",
},
{
Key: "DatastoreFileMovedEvent",
Description: "File or directory moved to datastore",
Category: "info",
FullFormat: "Move of file or directory {{.SourceFile}} from {{.SourceDatastore.Name}} to {{.Datastore.Name}} as {{.TargetFile}}",
},
{
Key: "DatastoreFileCopiedEvent",
Description: "File or directory copied to datastore",
Category: "info",
FullFormat: "Copy of file or directory {{.SourceFile}} from {{.SourceDatastore.Name}} to {{.Datastore.Name}} as {{.TargetFile}}",
},
{
Key: "DatastoreFileDeletedEvent",
Description: "File or directory deleted",
Category: "info",
FullFormat: "Deletion of file or directory {{.TargetFile}} from {{.Datastore.Name}} was initiated",
},
{
Key: "EnteringMaintenanceModeEvent",
Description: "Entering maintenance mode",
Category: "info",
FullFormat: "Host {{.Host.Name}} in {{.Datacenter.Name}} has started to enter maintenance mode",
},
{
Key: "EnteredMaintenanceModeEvent",
Description: "Entered maintenance mode",
Category: "info",
FullFormat: "Host {{.Host.Name}} in {{.Datacenter.Name}} has entered maintenance mode",
},
{
Key: "ExitMaintenanceModeEvent",
Description: "Exit maintenance mode",
Category: "info",
FullFormat: "Host {{.Host.Name}} in {{.Datacenter.Name}} has exited maintenance mode",
},
{
Key: "VmSuspendedEvent",
Description: "VM suspended",
Category: "info",
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is suspended",
},
{
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",
},
{
Key: "VmBeingMigratedEvent",
Description: "VM migrating",
Category: "info",
FullFormat: "Relocating {{.Vm.Name}} from {{.Host.Name}, {{.Ds.Name}} in {{.Datacenter.Name}} to {{.DestHost.Name}, {{.DestDatastore.Name}} in {{.DestDatacenter.Name}}",
},
{
Key: "VmMacAssignedEvent",
Description: "VM MAC assigned",
Category: "info",
FullFormat: "New MAC address ({{.Mac}}) assigned to adapter {{.Adapter}} for {{.Vm.Name}}",
},
{
Key: "VmRegisteredEvent",
Description: "VM registered",
Category: "info",
FullFormat: "Registered {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmReconfiguredEvent",
Description: "VM reconfigured",
Category: "info",
FullFormat: "Reconfigured {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmGuestRebootEvent",
Description: "Guest reboot",
Category: "info",
FullFormat: "Guest OS reboot for {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmBeingClonedEvent",
Description: "VM being cloned",
Category: "info",
FullFormat: "Cloning {{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} to {{.DestName}} on host {{.DestHost.Name}}",
},
{
Key: "VmClonedEvent",
Description: "VM cloned",
Category: "info",
FullFormat: "Clone of {{.SourceVm.Name}} completed",
},
{
Key: "VmBeingDeployedEvent",
Description: "Deploying VM",
Category: "info",
FullFormat: "Deploying {{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} from template {{.SrcTemplate.Name}}",
},
{
Key: "VmDeployedEvent",
Description: "VM deployed",
Category: "info",
FullFormat: "Template {{.SrcTemplate.Name}} deployed on host {{.Host.Name}}",
},
{
Key: "VmInstanceUuidAssignedEvent",
Description: "Assign a new instance UUID",
Category: "info",
FullFormat: "Assign a new instance UUID ({{.InstanceUuid}}) to {{.Vm.Name}}",
},
{
Key: "VmPoweredOnEvent",
Description: "VM powered on",
Category: "info",
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is powered on",
},
{
Key: "VmStartingEvent",
Description: "VM starting",
Category: "info",
FullFormat: "{{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} is starting",
},
{
Key: "VmSuspendingEvent",
Description: "VM being suspended",
Category: "info",
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is being suspended",
},
{
Key: "VmResumingEvent",
Description: "VM resuming",
Category: "info",
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is resumed",
},
{
Key: "VmBeingCreatedEvent",
Description: "Creating VM",
Category: "info",
FullFormat: "Creating {{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmCreatedEvent",
Description: "VM created",
Category: "info",
FullFormat: "Created virtual machine {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmRemovedEvent",
Description: "VM removed",
Category: "info",
FullFormat: "Removed {{.Vm.Name}} on {{.Host.Name}} from {{.Datacenter.Name}}",
},
{
Key: "VmResettingEvent",
Description: "VM resetting",
Category: "info",
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is reset",
},
{
Key: "VmGuestShutdownEvent",
Description: "Guest OS shut down",
Category: "info",
FullFormat: "Guest OS shut down for {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmUuidAssignedEvent",
Description: "VM UUID assigned",
Category: "info",
FullFormat: "Assigned new BIOS UUID ({{.Uuid}}) to {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmPoweredOffEvent",
Description: "VM powered off",
Category: "info",
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is powered off",
},
{
Key: "VmRelocatedEvent",
Description: "VM relocated",
Category: "info",
FullFormat: "Completed the relocation of the virtual machine",
},
{
Key: "DrsVmMigratedEvent",
Description: "DRS VM migrated",
Category: "info",
FullFormat: "DRS migrated {{.Vm.Name}} from {{.SourceHost.Name}} to {{.Host.Name}} in cluster {{.ComputeResource.Name}} in {{.Datacenter.Name}}",
},
{
Key: "DrsVmPoweredOnEvent",
Description: "DRS VM powered on",
Category: "info",
FullFormat: "DRS powered On {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
}

View File

@ -0,0 +1,386 @@
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"bytes"
"container/ring"
"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"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
var (
maxPageSize = 1000
logEvents = false
)
type EventManager struct {
mo.EventManager
root types.ManagedObjectReference
page *ring.Ring
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) CreateCollectorForEvents(ctx *Context, req *types.CreateCollectorForEvents) soap.HasFault {
body := new(methods.CreateCollectorForEventsBody)
size, err := validatePageSize(req.Filter.MaxCount)
if err != nil {
body.Fault_ = err
return body
}
if len(m.collectors) >= int(m.MaxCollector) {
body.Fault_ = Fault("Too many event collectors to create", new(types.InvalidState))
return body
}
collector := &EventHistoryCollector{
m: m,
page: ring.New(size),
}
collector.Filter = req.Filter
collector.fillPage(size)
ref := ctx.Session.Put(collector).Reference()
m.collectors[ref] = collector
body.Res = &types.CreateCollectorForEventsResponse{
Returnval: ref,
}
return body
}
// formatMessage applies the EventDescriptionEventDetail.FullFormat template to the given event's FullFormattedMessage field.
func (m *EventManager) formatMessage(event types.BaseEvent) {
id := reflect.ValueOf(event).Elem().Type().Name()
e := event.GetEvent()
t, ok := m.templates[id]
if !ok {
for _, info := range m.Description.EventInfo {
if info.Key == id {
t = template.Must(template.New(id).Parse(info.FullFormat))
m.templates[id] = t
break
}
}
}
var buf bytes.Buffer
if err := t.Execute(&buf, event); err != nil {
log.Print(err)
}
e.FullFormattedMessage = buf.String()
if logEvents {
log.Printf("[%s] %s", id, e.FullFormattedMessage)
}
}
func (m *EventManager) PostEvent(ctx *Context, req *types.PostEvent) soap.HasFault {
m.key++
event := req.EventToPost.GetEvent()
event.Key = m.key
event.ChainId = event.Key
event.CreatedTime = time.Now()
event.UserName = ctx.Session.UserName
m.page = m.page.Next()
m.page.Value = req.EventToPost
m.formatMessage(req.EventToPost)
for _, c := range m.collectors {
if c.eventMatches(req.EventToPost) {
c.page = c.page.Next()
c.page.Value = event
}
}
return &methods.PostEventBody{
Res: new(types.PostEventResponse),
}
}
type EventHistoryCollector struct {
mo.EventHistoryCollector
m *EventManager
page *ring.Ring
}
// doEntityEventArgument calls f for each entity argument in the event.
// If f returns true, the iteration stops.
func doEntityEventArgument(event types.BaseEvent, f func(types.ManagedObjectReference, *types.EntityEventArgument) bool) bool {
e := event.GetEvent()
if arg := e.Vm; arg != nil {
if f(arg.Vm, &arg.EntityEventArgument) {
return true
}
}
if arg := e.Host; arg != nil {
if f(arg.Host, &arg.EntityEventArgument) {
return true
}
}
if arg := e.ComputeResource; arg != nil {
if f(arg.ComputeResource, &arg.EntityEventArgument) {
return true
}
}
if arg := e.Ds; arg != nil {
if f(arg.Datastore, &arg.EntityEventArgument) {
return true
}
}
if arg := e.Net; arg != nil {
if f(arg.Network, &arg.EntityEventArgument) {
return true
}
}
if arg := e.Dvs; arg != nil {
if f(arg.Dvs, &arg.EntityEventArgument) {
return true
}
}
if arg := e.Datacenter; arg != nil {
if f(arg.Datacenter, &arg.EntityEventArgument) {
return true
}
}
return false
}
// eventFilterSelf returns true if self is one of the entity arguments in the event.
func eventFilterSelf(event types.BaseEvent, self types.ManagedObjectReference) bool {
return doEntityEventArgument(event, func(ref types.ManagedObjectReference, _ *types.EntityEventArgument) bool {
return self == ref
})
}
// eventFilterChildren returns true if a child of self is one of the entity arguments in the event.
func eventFilterChildren(event types.BaseEvent, self types.ManagedObjectReference) bool {
return doEntityEventArgument(event, func(ref types.ManagedObjectReference, _ *types.EntityEventArgument) bool {
seen := false
var match func(types.ManagedObjectReference)
match = func(child types.ManagedObjectReference) {
if child == self {
seen = true
return
}
walk(child, match)
}
walk(ref, match)
return seen
})
}
// entityMatches returns true if the spec Entity filter matches the event.
func (c *EventHistoryCollector) entityMatches(event types.BaseEvent, spec *types.EventFilterSpec) bool {
e := spec.Entity
if e == nil {
return true
}
isRootFolder := c.m.root == e.Entity
switch e.Recursion {
case types.EventFilterSpecRecursionOptionSelf:
return isRootFolder || eventFilterSelf(event, e.Entity)
case types.EventFilterSpecRecursionOptionChildren:
return eventFilterChildren(event, e.Entity)
case types.EventFilterSpecRecursionOptionAll:
if isRootFolder || eventFilterSelf(event, e.Entity) {
return true
}
return eventFilterChildren(event, e.Entity)
}
return false
}
// typeMatches returns true if one of the spec EventTypeId types matches the event.
func (c *EventHistoryCollector) typeMatches(event types.BaseEvent, spec *types.EventFilterSpec) bool {
if len(spec.EventTypeId) == 0 {
return true
}
matches := func(name string) bool {
for _, id := range spec.EventTypeId {
if id == name {
return true
}
}
return false
}
kind := reflect.ValueOf(event).Elem().Type()
if matches(kind.Name()) {
return true // concrete type
}
field, ok := kind.FieldByNameFunc(matches)
if ok {
return field.Anonymous // base type (embedded field)
}
return false
}
// 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
}
// TODO: spec.Time, spec.UserName, etc
return c.entityMatches(event, &spec)
}
// filePage copies the manager's latest events into the collector's page with Filter applied.
func (c *EventHistoryCollector) fillPage(size int) {
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
}
if c.eventMatches(event) {
page.Value = event
page = page.Prev()
matches++
if matches == size {
break
}
}
}
}
func validatePageSize(count int32) (int, *soap.Fault) {
size := int(count)
if size == 0 {
size = 10 // defaultPageSize
} else if size < 0 || size > maxPageSize {
return -1, Fault("", &types.InvalidArgument{InvalidProperty: "maxCount"})
}
return size, nil
}
func (c *EventHistoryCollector) SetCollectorPageSize(ctx *Context, req *types.SetCollectorPageSize) soap.HasFault {
body := new(methods.SetCollectorPageSizeBody)
size, err := validatePageSize(req.MaxCount)
if err != nil {
body.Fault_ = err
return body
}
ctx.WithLock(c.m, func() {
c.fillPage(size)
})
body.Res = new(types.SetCollectorPageSizeResponse)
return body
}
func (c *EventHistoryCollector) DestroyCollector(ctx *Context, req *types.DestroyCollector) soap.HasFault {
ctx.Session.Remove(req.This)
ctx.WithLock(c.m, func() {
delete(c.m.collectors, req.This)
})
return &methods.DestroyCollectorBody{
Res: new(types.DestroyCollectorResponse),
}
}
func (c *EventHistoryCollector) Get() mo.Reference {
clone := *c
c.page.Do(func(val interface{}) {
if val == nil {
return
}
clone.LatestPage = append(clone.LatestPage, val.(types.BaseEvent))
})
return &clone
}

View File

@ -160,6 +160,7 @@ func (f *FileManager) MakeDirectory(req *types.MakeDirectory) soap.HasFault {
return body
}
body.Res = new(types.MakeDirectoryResponse)
return body
}

View File

@ -20,7 +20,6 @@ import (
"fmt"
"math/rand"
"path"
"sync"
"github.com/google/uuid"
@ -32,12 +31,17 @@ import (
type Folder struct {
mo.Folder
}
m sync.Mutex
func (f *Folder) eventArgument() types.FolderEventArgument {
return types.FolderEventArgument{
Folder: f.Self,
EntityEventArgument: types.EntityEventArgument{Name: f.Name},
}
}
// update references when objects are added/removed from a Folder
func (f *Folder) update(o mo.Reference, u func(types.ManagedObjectReference, []types.ManagedObjectReference) []types.ManagedObjectReference) {
func (f *Folder) update(o mo.Reference, u func(mo.Reference, *[]types.ManagedObjectReference, types.ManagedObjectReference)) {
ref := o.Reference()
if f.Parent == nil {
@ -53,9 +57,9 @@ func (f *Folder) update(o mo.Reference, u func(types.ManagedObjectReference, []t
switch ref.Type {
case "Network", "DistributedVirtualSwitch", "DistributedVirtualPortgroup":
dc.Network = u(ref, dc.Network)
u(dc, &dc.Network, ref)
case "Datastore":
dc.Datastore = u(ref, dc.Datastore)
u(dc, &dc.Datastore, ref)
}
}
@ -70,12 +74,9 @@ func networkSummary(n *mo.Network) *types.NetworkSummary {
func (f *Folder) putChild(o mo.Entity) {
Map.PutEntity(f, o)
f.m.Lock()
defer f.m.Unlock()
f.ChildEntity = append(f.ChildEntity, o.Reference())
f.ChildEntity = AddReference(o.Reference(), f.ChildEntity)
f.update(o, AddReference)
f.update(o, Map.AddReference)
switch e := o.(type) {
case *mo.Network:
@ -90,12 +91,9 @@ func (f *Folder) putChild(o mo.Entity) {
func (f *Folder) removeChild(o mo.Reference) {
Map.Remove(o.Reference())
f.m.Lock()
defer f.m.Unlock()
RemoveReference(&f.ChildEntity, o.Reference())
f.ChildEntity = RemoveReference(o.Reference(), f.ChildEntity)
f.update(o, RemoveReference)
f.update(o, Map.RemoveReference)
}
func (f *Folder) hasChildType(kind string) bool {
@ -195,7 +193,7 @@ func (p *StoragePod) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFa
return (&Folder{Folder: p.Folder}).MoveIntoFolderTask(c)
}
func (f *Folder) CreateDatacenter(c *types.CreateDatacenter) soap.HasFault {
func (f *Folder) CreateDatacenter(ctx *Context, c *types.CreateDatacenter) soap.HasFault {
r := &methods.CreateDatacenterBody{}
if f.hasChildType("Datacenter") && f.hasChildType("Folder") {
@ -210,6 +208,15 @@ func (f *Folder) CreateDatacenter(c *types.CreateDatacenter) soap.HasFault {
r.Res = &types.CreateDatacenterResponse{
Returnval: dc.Self,
}
ctx.postEvent(&types.DatacenterCreatedEvent{
DatacenterEvent: types.DatacenterEvent{
Event: types.Event{
Datacenter: datacenterEventArgument(dc),
},
},
Parent: f.eventArgument(),
})
} else {
r.Fault_ = f.typeNotSupported()
}
@ -240,6 +247,7 @@ func (f *Folder) CreateClusterEx(c *types.CreateClusterEx) soap.HasFault {
type createVM struct {
*Folder
ctx *Context
req *types.CreateVM_Task
register bool
@ -291,27 +299,50 @@ func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
c.Folder.putChild(vm)
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
host.Vm = append(host.Vm, vm.Self)
Map.AppendReference(host, &host.Vm, vm.Self)
for i := range vm.Datastore {
ds := Map.Get(vm.Datastore[i]).(*Datastore)
ds.Vm = append(ds.Vm, vm.Self)
Map.AppendReference(ds, &ds.Vm, vm.Self)
}
switch rp := Map.Get(*vm.ResourcePool).(type) {
case *ResourcePool:
rp.Vm = append(rp.Vm, vm.Self)
case *VirtualApp:
rp.Vm = append(rp.Vm, vm.Self)
}
pool := 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:
rp.Vm = append(rp.Vm, vm.Self)
}
})
event := vm.event()
c.ctx.postEvent(
&types.VmBeingCreatedEvent{
VmEvent: event,
ConfigSpec: &c.req.Config,
},
&types.VmInstanceUuidAssignedEvent{
VmEvent: event,
InstanceUuid: vm.Config.InstanceUuid,
},
&types.VmUuidAssignedEvent{
VmEvent: event,
Uuid: vm.Config.Uuid,
},
&types.VmCreatedEvent{
VmEvent: event,
},
)
return vm.Reference(), nil
}
func (f *Folder) CreateVMTask(c *types.CreateVM_Task) soap.HasFault {
func (f *Folder) CreateVMTask(ctx *Context, c *types.CreateVM_Task) soap.HasFault {
return &methods.CreateVM_TaskBody{
Res: &types.CreateVM_TaskResponse{
Returnval: NewTask(&createVM{f, c, false}).Run(),
Returnval: NewTask(&createVM{f, ctx, c, false}).Run(),
},
}
}
@ -319,6 +350,7 @@ func (f *Folder) CreateVMTask(c *types.CreateVM_Task) soap.HasFault {
type registerVM struct {
*Folder
ctx *Context
req *types.RegisterVM_Task
}
@ -367,6 +399,7 @@ func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
create := NewTask(&createVM{
Folder: c.Folder,
register: true,
ctx: c.ctx,
req: &types.CreateVM_Task{
This: c.Folder.Reference(),
Config: types.VirtualMachineConfigSpec{
@ -389,10 +422,12 @@ func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
return create.Info.Result, nil
}
func (f *Folder) RegisterVMTask(c *types.RegisterVM_Task) soap.HasFault {
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, c}).Run(),
Returnval: NewTask(&registerVM{f, ctx, c}).Run(),
},
}
}
@ -445,6 +480,33 @@ func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault {
Description: spec.Description,
}
configInfo := &types.VMwareDVSConfigInfo{
DVSConfigInfo: types.DVSConfigInfo{
Uuid: dvs.Uuid,
Name: spec.Name,
ConfigVersion: spec.ConfigVersion,
NumStandalonePorts: spec.NumStandalonePorts,
MaxPorts: spec.MaxPorts,
UplinkPortPolicy: spec.UplinkPortPolicy,
UplinkPortgroup: spec.UplinkPortgroup,
DefaultPortConfig: spec.DefaultPortConfig,
ExtensionKey: spec.ExtensionKey,
Description: spec.Description,
Policy: spec.Policy,
VendorSpecificConfig: spec.VendorSpecificConfig,
SwitchIpAddress: spec.SwitchIpAddress,
DefaultProxySwitchMaxNumPorts: spec.DefaultProxySwitchMaxNumPorts,
InfrastructureTrafficResourceConfig: spec.InfrastructureTrafficResourceConfig,
NetworkResourceControlVersion: spec.NetworkResourceControlVersion,
},
}
if spec.Contact != nil {
configInfo.Contact = *spec.Contact
}
dvs.Config = configInfo
if dvs.Summary.ProductInfo == nil {
product := Map.content().About
dvs.Summary.ProductInfo = &types.DistributedVirtualSwitchProductSpec{

View File

@ -72,7 +72,7 @@ func (dss *HostDatastoreSystem) add(ds *Datastore) *soap.Fault {
dss.Datastore = append(dss.Datastore, ds.Self)
dss.Host.Datastore = dss.Datastore
parent := hostParent(dss.Host)
parent.Datastore = AddReference(ds.Self, parent.Datastore)
Map.AddReference(parent, &parent.Datastore, ds.Self)
browser := &HostDatastoreBrowser{}
browser.Datastore = dss.Datastore

View File

@ -0,0 +1,80 @@
/*
Copyright (c) 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 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"
"github.com/vmware/govmomi/vim25/types"
)
// As of vSphere API 5.1, local groups operations are deprecated, so it's not supported here.
type HostLocalAccountManager struct {
mo.HostLocalAccountManager
}
func NewHostLocalAccountManager(ref types.ManagedObjectReference) object.Reference {
m := &HostLocalAccountManager{}
m.Self = ref
return m
}
func (h *HostLocalAccountManager) CreateUser(req *types.CreateUser) soap.HasFault {
spec := req.User.GetHostAccountSpec()
userDirectory := Map.UserDirectory()
found := userDirectory.search(true, false, compareFunc(spec.Id, true))
if len(found) > 0 {
return &methods.CreateUserBody{
Fault_: Fault("", &types.AlreadyExists{}),
}
}
userDirectory.addUser(spec.Id)
return &methods.CreateUserBody{
Res: &types.CreateUserResponse{},
}
}
func (h *HostLocalAccountManager) RemoveUser(req *types.RemoveUser) soap.HasFault {
userDirectory := Map.UserDirectory()
found := userDirectory.search(true, false, compareFunc(req.UserName, true))
if len(found) == 0 {
return &methods.RemoveUserBody{
Fault_: Fault("", &types.UserNotFound{}),
}
}
userDirectory.removeUser(req.UserName)
return &methods.RemoveUserBody{
Res: &types.RemoveUserResponse{},
}
}
func (h *HostLocalAccountManager) UpdateUser(req *types.UpdateUser) soap.HasFault {
return &methods.CreateUserBody{
Res: &types.CreateUserResponse{},
}
}

View File

@ -71,6 +71,22 @@ func NewHostSystem(host mo.HostSystem) *HostSystem {
return hs
}
func (h *HostSystem) eventArgument() *types.HostEventArgument {
return &types.HostEventArgument{
Host: h.Self,
EntityEventArgument: types.EntityEventArgument{Name: h.Name},
}
}
func (h *HostSystem) eventArgumentParent() *types.ComputeResourceEventArgument {
parent := hostParent(&h.HostSystem)
return &types.ComputeResourceEventArgument{
ComputeResource: parent.Self,
EntityEventArgument: types.EntityEventArgument{Name: parent.Name},
}
}
func hostParent(host *mo.HostSystem) *mo.ComputeResource {
switch parent := Map.Get(*host.Parent).(type) {
case *mo.ComputeResource:
@ -137,9 +153,15 @@ func CreateStandaloneHost(f *Folder, spec types.HostConnectSpec) (*HostSystem, t
summary := new(types.ComputeResourceSummary)
addComputeResource(summary, host)
cr := &mo.ComputeResource{Summary: summary}
cr := &mo.ComputeResource{
ConfigurationEx: &types.ComputeResourceConfigInfo{
VmSwapPlacement: string(types.VirtualMachineConfigInfoSwapPlacementTypeVmDirectory),
},
Summary: summary,
}
Map.PutEntity(cr, Map.NewEntity(host))
host.Summary.Host = &host.Self
Map.PutEntity(cr, Map.NewEntity(pool))

View File

@ -34,13 +34,13 @@ import (
// This is a simple helper for tests running against a simulator, to populate an inventory
// with commonly used models.
type Model struct {
Service *Service
Service *Service `json:"-"`
ServiceContent types.ServiceContent
RootFolder mo.Folder
ServiceContent types.ServiceContent `json:"-"`
RootFolder mo.Folder `json:"-"`
// Autostart will power on Model created VMs when true
Autostart bool
Autostart bool `json:"-"`
// Datacenter specifies the number of Datacenter entities to create
Datacenter int
@ -49,13 +49,13 @@ type Model struct {
Portgroup int
// Host specifies the number of standalone HostSystems entities to create per Datacenter
Host int
Host int `json:",omitempty"`
// Cluster specifies the number of ClusterComputeResource entities to create per Datacenter
Cluster int
// ClusterHost specifies the number of HostSystems entities to create within a Cluster
ClusterHost int
ClusterHost int `json:",omitempty"`
// Pool specifies the number of ResourcePool entities to create per Cluster
Pool int
@ -233,6 +233,7 @@ func (m *Model) Create() error {
cdrom, _ := devices.CreateCdrom(ide.(*types.VirtualIDEController))
disk := devices.CreateDisk(scsi.(types.BaseVirtualController), ds,
config.Files.VmPathName+" "+path.Join(name, "disk1.vmdk"))
disk.CapacityInKB = 1024
devices = append(devices, scsi, cdrom, disk, &nic)

View File

@ -57,3 +57,42 @@ func (m *OptionManager) QueryOptions(req *types.QueryOptions) soap.HasFault {
return body
}
func (m *OptionManager) find(key string) *types.OptionValue {
for _, opt := range m.Setting {
setting := opt.GetOptionValue()
if setting.Key == key {
return setting
}
}
return nil
}
func (m *OptionManager) UpdateOptions(req *types.UpdateOptions) soap.HasFault {
body := new(methods.UpdateOptionsBody)
for _, change := range req.ChangedValue {
setting := change.GetOptionValue()
// We don't currently include the entire list of default settings for ESX and vCenter,
// this prefix is currently used to test the failure path.
// Real vCenter seems to only allow new options if Key has a "config." prefix.
// TODO: consider behaving the same, which would require including 2 long lists of options in vpx.Setting and esx.Setting
if strings.HasPrefix(setting.Key, "ENOENT.") {
body.Fault_ = Fault("", &types.InvalidName{Name: setting.Key})
return body
}
opt := m.find(setting.Key)
if opt != nil {
// This is an existing option.
opt.Value = setting.Value
continue
}
m.Setting = append(m.Setting, change)
}
body.Res = new(types.UpdateOptionsResponse)
return body
}

View File

@ -53,23 +53,12 @@ func (s *DistributedVirtualPortgroup) ReconfigureDVPortgroupTask(req *types.Reco
func (s *DistributedVirtualPortgroup) DestroyTask(req *types.Destroy_Task) soap.HasFault {
task := CreateTask(s, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
vswitch := Map.Get(*s.Config.DistributedVirtualSwitch).(*DistributedVirtualSwitch)
for i, pg := range vswitch.Portgroup {
if pg.Reference() == s.Reference() {
vswitch.Portgroup = append(vswitch.Portgroup[:i], vswitch.Portgroup[i+1:]...)
break
}
}
Map.RemoveReference(vswitch, &vswitch.Portgroup, s.Reference())
Map.removeString(vswitch, &vswitch.Summary.PortgroupName, s.Name)
f := Map.getEntityParent(vswitch, "Folder").(*Folder)
f.removeChild(s.Reference())
for i, name := range vswitch.Summary.PortgroupName {
if name == s.Name {
vswitch.Summary.PortgroupName = append(vswitch.Summary.PortgroupName[:i],
vswitch.Summary.PortgroupName[i+1:]...)
}
}
return nil, nil
})

View File

@ -43,12 +43,35 @@ func NewPropertyCollector(ref types.ManagedObjectReference) object.Reference {
var errMissingField = errors.New("missing field")
var errEmptyField = errors.New("empty field")
func getObject(ref types.ManagedObjectReference) (reflect.Value, bool) {
obj := Map.Get(ref)
func getObject(ctx *Context, ref types.ManagedObjectReference) (reflect.Value, bool) {
var obj mo.Reference
if ctx.Session == nil {
// Even without permissions to access an object or specific fields, RetrieveProperties
// returns an ObjectContent response as long as the object exists. See retrieveResult.add()
obj = Map.Get(ref)
} else {
obj = ctx.Session.Get(ref)
}
if obj == nil {
return reflect.Value{}, false
}
if ctx.Session == nil && ref.Type == "SessionManager" {
// RetrieveProperties on SessionManager without a session always returns empty,
// rather than MissingSet + Fault.NotAuthenticated for each field.
obj = &mo.SessionManager{Self: ref}
}
// For objects that use internal types that differ from that of the vim25/mo field types.
// See EventHistoryCollector for example.
type get interface {
Get() mo.Reference
}
if o, ok := obj.(get); ok {
obj = o.Get()
}
rval := reflect.ValueOf(obj).Elem()
rtype := rval.Type()
@ -110,7 +133,6 @@ func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{}
func fieldValue(rval reflect.Value, p string) (interface{}, error) {
var value interface{}
fields := strings.Split(p, ".")
for i, name := range fields {
@ -170,7 +192,7 @@ func isEmpty(rval reflect.Value) bool {
switch rval.Kind() {
case reflect.Ptr:
return rval.IsNil()
case reflect.String, reflect.Slice:
case reflect.String:
return rval.Len() == 0
}
@ -200,7 +222,27 @@ type retrieveResult struct {
specs map[string]*types.TraversalSpec
}
func (rr *retrieveResult) collectAll(rval reflect.Value, rtype reflect.Type, content *types.ObjectContent) {
func (rr *retrieveResult) add(ctx *Context, name string, val types.AnyType, content *types.ObjectContent) {
if ctx.Session != nil {
content.PropSet = append(content.PropSet, types.DynamicProperty{
Name: name,
Val: val,
})
return
}
content.MissingSet = append(content.MissingSet, types.MissingProperty{
Path: name,
Fault: types.LocalizedMethodFault{Fault: &types.NotAuthenticated{
NoPermission: types.NoPermission{
Object: content.Obj,
PrivilegeId: "System.Read",
}},
},
})
}
func (rr *retrieveResult) collectAll(ctx *Context, rval reflect.Value, rtype reflect.Type, content *types.ObjectContent) {
for i := 0; i < rval.NumField(); i++ {
val := rval.Field(i)
@ -212,18 +254,15 @@ func (rr *retrieveResult) collectAll(rval reflect.Value, rtype reflect.Type, con
if f.Anonymous {
// recurse into embedded field
rr.collectAll(val, f.Type, content)
rr.collectAll(ctx, val, f.Type, content)
continue
}
content.PropSet = append(content.PropSet, types.DynamicProperty{
Name: lcFirst(f.Name),
Val: fieldValueInterface(f, val),
})
rr.add(ctx, lcFirst(f.Name), fieldValueInterface(f, val), content)
}
}
func (rr *retrieveResult) collectFields(rval reflect.Value, fields []string, content *types.ObjectContent) {
func (rr *retrieveResult) collectFields(ctx *Context, rval reflect.Value, fields []string, content *types.ObjectContent) {
seen := make(map[string]bool)
for i := range content.PropSet {
@ -240,12 +279,7 @@ func (rr *retrieveResult) collectFields(rval reflect.Value, fields []string, con
val, err := fieldValue(rval, name)
if err == nil {
prop := types.DynamicProperty{
Name: name,
Val: val,
}
content.PropSet = append(content.PropSet, prop)
rr.add(ctx, name, val, content)
continue
}
@ -263,7 +297,7 @@ func (rr *retrieveResult) collectFields(rval reflect.Value, fields []string, con
}
}
func (rr *retrieveResult) collect(ref types.ManagedObjectReference) {
func (rr *retrieveResult) collect(ctx *Context, ref types.ManagedObjectReference) {
if rr.collected[ref] {
return
}
@ -272,7 +306,7 @@ func (rr *retrieveResult) collect(ref types.ManagedObjectReference) {
Obj: ref,
}
rval, ok := getObject(ref)
rval, ok := getObject(ctx, ref)
if !ok {
// Possible if a test uses Map.Remove instead of Destroy_Task
log.Printf("object %s no longer exists", ref)
@ -293,11 +327,11 @@ func (rr *retrieveResult) collect(ref types.ManagedObjectReference) {
}
if isTrue(p.All) {
rr.collectAll(rval, rtype, &content)
rr.collectAll(ctx, rval, rtype, &content)
continue
}
rr.collectFields(rval, p.PathSet, &content)
rr.collectFields(ctx, rval, p.PathSet, &content)
}
}
@ -308,10 +342,9 @@ func (rr *retrieveResult) collect(ref types.ManagedObjectReference) {
rr.collected[ref] = true
}
func (rr *retrieveResult) selectSet(obj reflect.Value, s []types.BaseSelectionSpec, refs *[]types.ManagedObjectReference) types.BaseMethodFault {
func (rr *retrieveResult) selectSet(ctx *Context, obj reflect.Value, s []types.BaseSelectionSpec, refs *[]types.ManagedObjectReference) types.BaseMethodFault {
for _, ss := range s {
ts, ok := ss.(*types.TraversalSpec)
if ok {
if ts.Name != "" {
rr.specs[ts.Name] = ts
@ -335,9 +368,9 @@ func (rr *retrieveResult) selectSet(obj reflect.Value, s []types.BaseSelectionSp
*refs = append(*refs, ref)
}
rval, ok := getObject(ref)
rval, ok := getObject(ctx, ref)
if ok {
if err := rr.selectSet(rval, ts.SelectSet, refs); err != nil {
if err := rr.selectSet(ctx, rval, ts.SelectSet, refs); err != nil {
return err
}
}
@ -347,7 +380,7 @@ func (rr *retrieveResult) selectSet(obj reflect.Value, s []types.BaseSelectionSp
return nil
}
func (pc *PropertyCollector) collect(r *types.RetrievePropertiesEx) (*types.RetrieveResult, types.BaseMethodFault) {
func (pc *PropertyCollector) collect(ctx *Context, r *types.RetrievePropertiesEx) (*types.RetrieveResult, types.BaseMethodFault) {
var refs []types.ManagedObjectReference
rr := &retrieveResult{
@ -360,8 +393,7 @@ func (pc *PropertyCollector) collect(r *types.RetrievePropertiesEx) (*types.Retr
// Select object references
for _, spec := range r.SpecSet {
for _, o := range spec.ObjectSet {
rval, ok := getObject(o.Obj)
rval, ok := getObject(ctx, o.Obj)
if !ok {
if isFalse(spec.ReportMissingObjectsInResults) {
return nil, &types.ManagedObjectNotFound{Obj: o.Obj}
@ -373,27 +405,27 @@ func (pc *PropertyCollector) collect(r *types.RetrievePropertiesEx) (*types.Retr
refs = append(refs, o.Obj)
}
if err := rr.selectSet(rval, o.SelectSet, &refs); err != nil {
if err := rr.selectSet(ctx, rval, o.SelectSet, &refs); err != nil {
return nil, err
}
}
}
for _, ref := range refs {
rr.collect(ref)
rr.collect(ctx, ref)
}
return rr.RetrieveResult, nil
}
func (pc *PropertyCollector) CreateFilter(c *types.CreateFilter) soap.HasFault {
func (pc *PropertyCollector) CreateFilter(ctx *Context, c *types.CreateFilter) soap.HasFault {
body := &methods.CreateFilterBody{}
filter := &PropertyFilter{pc: pc}
filter.PartialUpdates = c.PartialUpdates
filter.Spec = c.Spec
pc.Filter = append(pc.Filter, Map.Put(filter).Reference())
pc.Filter = append(pc.Filter, ctx.Session.Put(filter).Reference())
body.Res = &types.CreateFilterResponse{
Returnval: filter.Self,
@ -402,37 +434,37 @@ func (pc *PropertyCollector) CreateFilter(c *types.CreateFilter) soap.HasFault {
return body
}
func (pc *PropertyCollector) CreatePropertyCollector(c *types.CreatePropertyCollector) soap.HasFault {
func (pc *PropertyCollector) CreatePropertyCollector(ctx *Context, c *types.CreatePropertyCollector) soap.HasFault {
body := &methods.CreatePropertyCollectorBody{}
cpc := &PropertyCollector{}
body.Res = &types.CreatePropertyCollectorResponse{
Returnval: Map.Put(cpc).Reference(),
Returnval: ctx.Session.Put(cpc).Reference(),
}
return body
}
func (pc *PropertyCollector) DestroyPropertyCollector(c *types.DestroyPropertyCollector) soap.HasFault {
func (pc *PropertyCollector) DestroyPropertyCollector(ctx *Context, c *types.DestroyPropertyCollector) soap.HasFault {
body := &methods.DestroyPropertyCollectorBody{}
for _, ref := range pc.Filter {
filter := Map.Get(ref).(*PropertyFilter)
filter := ctx.Session.Get(ref).(*PropertyFilter)
filter.DestroyPropertyFilter(&types.DestroyPropertyFilter{This: ref})
}
Map.Remove(c.This)
ctx.Session.Remove(c.This)
body.Res = &types.DestroyPropertyCollectorResponse{}
return body
}
func (pc *PropertyCollector) RetrievePropertiesEx(r *types.RetrievePropertiesEx) soap.HasFault {
func (pc *PropertyCollector) RetrievePropertiesEx(ctx *Context, r *types.RetrievePropertiesEx) soap.HasFault {
body := &methods.RetrievePropertiesExBody{}
res, fault := pc.collect(r)
res, fault := pc.collect(ctx, r)
if fault != nil {
body.Fault_ = Fault("", fault)
@ -446,10 +478,10 @@ func (pc *PropertyCollector) RetrievePropertiesEx(r *types.RetrievePropertiesEx)
}
// RetrieveProperties is deprecated, but govmomi is still using it at the moment.
func (pc *PropertyCollector) RetrieveProperties(r *types.RetrieveProperties) soap.HasFault {
func (pc *PropertyCollector) RetrieveProperties(ctx *Context, r *types.RetrieveProperties) soap.HasFault {
body := &methods.RetrievePropertiesBody{}
res := pc.RetrievePropertiesEx(&types.RetrievePropertiesEx{
res := pc.RetrievePropertiesEx(ctx, &types.RetrievePropertiesEx{
This: r.This,
SpecSet: r.SpecSet,
})
@ -469,7 +501,7 @@ func (pc *PropertyCollector) CancelWaitForUpdates(r *types.CancelWaitForUpdates)
return &methods.CancelWaitForUpdatesBody{Res: new(types.CancelWaitForUpdatesResponse)}
}
func (pc *PropertyCollector) WaitForUpdatesEx(r *types.WaitForUpdatesEx) soap.HasFault {
func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpdatesEx) soap.HasFault {
body := &methods.WaitForUpdatesExBody{}
// At the moment we need to support Task completion. Handlers can simply set the Task
@ -485,12 +517,12 @@ func (pc *PropertyCollector) WaitForUpdatesEx(r *types.WaitForUpdatesEx) soap.Ha
}
for _, ref := range pc.Filter {
filter := Map.Get(ref).(*PropertyFilter)
filter := ctx.Session.Get(ref).(*PropertyFilter)
r := &types.RetrievePropertiesEx{}
r.SpecSet = append(r.SpecSet, filter.Spec)
res, fault := pc.collect(r)
res, fault := pc.collect(ctx, r)
if fault != nil {
body.Fault_ = Fault("", fault)
return body
@ -528,10 +560,10 @@ func (pc *PropertyCollector) WaitForUpdatesEx(r *types.WaitForUpdatesEx) soap.Ha
}
// WaitForUpdates is deprecated, but pyvmomi is still using it at the moment.
func (pc *PropertyCollector) WaitForUpdates(r *types.WaitForUpdates) soap.HasFault {
func (pc *PropertyCollector) WaitForUpdates(ctx *Context, r *types.WaitForUpdates) soap.HasFault {
body := &methods.WaitForUpdatesBody{}
res := pc.WaitForUpdatesEx(&types.WaitForUpdatesEx{
res := pc.WaitForUpdatesEx(ctx, &types.WaitForUpdatesEx{
This: r.This,
Version: r.Version,
})

View File

@ -32,7 +32,7 @@ type PropertyFilter struct {
func (f *PropertyFilter) DestroyPropertyFilter(c *types.DestroyPropertyFilter) soap.HasFault {
body := &methods.DestroyPropertyFilterBody{}
f.pc.Filter = RemoveReference(c.This, f.pc.Filter)
RemoveReference(&f.pc.Filter, c.This)
Map.Remove(c.This)

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,7 +17,9 @@ limitations under the License.
package simulator
import (
"encoding/json"
"fmt"
"os"
"reflect"
"strings"
"sync"
@ -55,6 +57,7 @@ type Registry struct {
m sync.Mutex
objects map[types.ManagedObjectReference]mo.Reference
handlers map[types.ManagedObjectReference]RegisterObject
locks map[types.ManagedObjectReference]sync.Locker
counter int
}
@ -63,6 +66,7 @@ func NewRegistry() *Registry {
r := &Registry{
objects: make(map[types.ManagedObjectReference]mo.Reference),
handlers: make(map[types.ManagedObjectReference]RegisterObject),
locks: make(map[types.ManagedObjectReference]sync.Locker),
}
return r
@ -99,6 +103,11 @@ func (r *Registry) newReference(item mo.Reference) types.ManagedObjectReference
return ref
}
func (r *Registry) setReference(item mo.Reference, ref types.ManagedObjectReference) {
// mo.Reference() returns a value, not a pointer so use reflect to set the Self field
reflect.ValueOf(item).Elem().FieldByName("Self").Set(reflect.ValueOf(ref))
}
// AddHandler adds a RegisterObject handler to the Registry.
func (r *Registry) AddHandler(h RegisterObject) {
r.handlers[h.Reference()] = h
@ -156,8 +165,7 @@ func (r *Registry) Put(item mo.Reference) mo.Reference {
ref := item.Reference()
if ref.Type == "" || ref.Value == "" {
ref = r.newReference(item)
// mo.Reference() returns a value, not a pointer so use reflect to set the Self field
reflect.ValueOf(item).Elem().FieldByName("Self").Set(reflect.ValueOf(ref))
r.setReference(item, ref)
}
if me, ok := item.(mo.Entity); ok {
@ -186,6 +194,7 @@ func (r *Registry) Remove(item types.ManagedObjectReference) {
delete(r.objects, item)
delete(r.handlers, item)
delete(r.locks, item)
}
// getEntityParent traverses up the inventory and returns the first object of type kind.
@ -278,29 +287,48 @@ func FindReference(refs []types.ManagedObjectReference, match ...types.ManagedOb
return nil
}
// RemoveReference returns a slice with ref removed from refs
func RemoveReference(ref types.ManagedObjectReference, refs []types.ManagedObjectReference) []types.ManagedObjectReference {
var result []types.ManagedObjectReference
for i, r := range refs {
if r == ref {
result = append(result, refs[i+1:]...)
break
}
result = append(result, r)
}
return result
// AppendReference appends the given refs to field.
func (r *Registry) AppendReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref ...types.ManagedObjectReference) {
r.WithLock(obj, func() {
*field = append(*field, ref...)
})
}
// AddReference returns a slice with ref appended if not already in refs.
func AddReference(ref types.ManagedObjectReference, refs []types.ManagedObjectReference) []types.ManagedObjectReference {
if FindReference(refs, ref) == nil {
return append(refs, ref)
}
// AddReference appends ref to field if not already in the given field.
func (r *Registry) AddReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
r.WithLock(obj, func() {
if FindReference(*field, ref) == nil {
*field = append(*field, ref)
}
})
}
return refs
// RemoveReference removes ref from the given field.
func RemoveReference(field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
for i, r := range *field {
if r == ref {
*field = append((*field)[:i], (*field)[i+1:]...)
break
}
}
}
// RemoveReference removes ref from the given field.
func (r *Registry) RemoveReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
r.WithLock(obj, func() {
RemoveReference(field, ref)
})
}
func (r *Registry) removeString(obj mo.Reference, field *[]string, val string) {
r.WithLock(obj, func() {
for i, name := range *field {
if name == val {
*field = append((*field)[:i], (*field)[i+1:]...)
break
}
}
})
}
func (r *Registry) content() types.ServiceContent {
@ -322,6 +350,11 @@ func (r *Registry) SearchIndex() *SearchIndex {
return r.Get(r.content().SearchIndex.Reference()).(*SearchIndex)
}
// EventManager returns the EventManager singleton
func (r *Registry) EventManager() *EventManager {
return r.Get(r.content().EventManager.Reference()).(*EventManager)
}
// FileManager returns the FileManager singleton
func (r *Registry) FileManager() *FileManager {
return r.Get(r.content().FileManager.Reference()).(*FileManager)
@ -336,3 +369,57 @@ func (r *Registry) VirtualDiskManager() *VirtualDiskManager {
func (r *Registry) ViewManager() *ViewManager {
return r.Get(r.content().ViewManager.Reference()).(*ViewManager)
}
// UserDirectory returns the UserDirectory singleton
func (r *Registry) UserDirectory() *UserDirectory {
return r.Get(r.content().UserDirectory.Reference()).(*UserDirectory)
}
// SessionManager returns the SessionManager singleton
func (r *Registry) SessionManager() *SessionManager {
return r.Get(r.content().SessionManager.Reference()).(*SessionManager)
}
func (r *Registry) MarshalJSON() ([]byte, error) {
r.m.Lock()
defer r.m.Unlock()
vars := struct {
Objects int
Locks int
}{
len(r.objects),
len(r.locks),
}
return json.Marshal(vars)
}
func (r *Registry) locker(obj mo.Reference) sync.Locker {
if mu, ok := obj.(sync.Locker); ok {
return mu
}
ref := obj.Reference()
r.m.Lock()
mu, ok := r.locks[ref]
if !ok {
mu = new(sync.Mutex)
r.locks[ref] = mu
}
r.m.Unlock()
return mu
}
var enableLocker = os.Getenv("VCSIM_LOCKER") != "false"
// WithLock holds a lock for the given object while then given function is run.
func (r *Registry) WithLock(obj mo.Reference, f func()) {
if enableLocker {
mu := r.locker(obj)
mu.Lock()
defer mu.Unlock()
}
f()
}

View File

@ -252,12 +252,13 @@ func (p *ResourcePool) CreateVApp(req *types.CreateVApp) soap.HasFault {
return body
}
func (a *VirtualApp) CreateChildVMTask(req *types.CreateChildVM_Task) soap.HasFault {
func (a *VirtualApp) CreateChildVMTask(ctx *Context, req *types.CreateChildVM_Task) soap.HasFault {
ctx.Caller = &a.Self
body := &methods.CreateChildVM_TaskBody{}
folder := Map.Get(*a.ParentFolder).(*Folder)
res := folder.CreateVMTask(&types.CreateVM_Task{
res := folder.CreateVMTask(ctx, &types.CreateVM_Task{
This: folder.Self,
Config: req.Config,
Host: req.Host,
@ -286,18 +287,19 @@ func (p *ResourcePool) DestroyTask(req *types.Destroy_Task) soap.HasFault {
parent := &pp.ResourcePool
// Remove child reference from rp
parent.ResourcePool = RemoveReference(req.This, parent.ResourcePool)
Map.RemoveReference(parent, &parent.ResourcePool, req.This)
// The grandchildren become children of the parent (rp)
parent.ResourcePool = append(parent.ResourcePool, p.ResourcePool.ResourcePool...)
Map.AppendReference(parent, &parent.ResourcePool, p.ResourcePool.ResourcePool...)
// And VMs move to the parent
vms := p.ResourcePool.Vm
for _, vm := range vms {
Map.Get(vm).(*VirtualMachine).ResourcePool = &parent.Self
for _, ref := range vms {
vm := Map.Get(ref).(*VirtualMachine)
Map.WithLock(vm, func() { vm.ResourcePool = &parent.Self })
}
parent.Vm = append(parent.Vm, vms...)
Map.AppendReference(parent, &parent.Vm, vms...)
Map.Remove(req.This)

View File

@ -62,6 +62,7 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service
NewLicenseManager(*s.Content.LicenseManager),
NewSearchIndex(*s.Content.SearchIndex),
NewViewManager(*s.Content.ViewManager),
NewEventManager(*s.Content.EventManager),
NewTaskManager(*s.Content.TaskManager),
NewUserDirectory(*s.Content.UserDirectory),
NewOptionManager(s.Content.Setting, setting),
@ -75,6 +76,10 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service
objects = append(objects, NewIpPoolManager(*s.Content.IpPoolManager))
}
if s.Content.AccountManager != nil {
objects = append(objects, NewHostLocalAccountManager(*s.Content.AccountManager))
}
for _, o := range objects {
Map.Put(o)
}

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,6 +17,10 @@ limitations under the License.
package simulator
import (
"context"
"fmt"
"net/http"
"strings"
"time"
"github.com/google/uuid"
@ -33,26 +37,30 @@ type SessionManager struct {
mo.SessionManager
ServiceHostName string
sessions map[string]Session
}
func NewSessionManager(ref types.ManagedObjectReference) object.Reference {
s := &SessionManager{}
s := &SessionManager{
sessions: make(map[string]Session),
}
s.Self = ref
return s
}
func (s *SessionManager) Login(login *types.Login) soap.HasFault {
func (s *SessionManager) Login(ctx *Context, login *types.Login) soap.HasFault {
body := &methods.LoginBody{}
if login.Locale == "" {
login.Locale = session.Locale
}
if login.UserName == "" || login.Password == "" {
body.Fault_ = Fault("Login failure", &types.InvalidLogin{})
if login.UserName == "" || login.Password == "" || ctx.Session != nil {
body.Fault_ = invalidLogin
} else {
body.Res = &types.LoginResponse{
Returnval: types.UserSession{
session := Session{
UserSession: types.UserSession{
Key: uuid.New().String(),
UserName: login.UserName,
FullName: login.UserName,
@ -61,16 +69,80 @@ func (s *SessionManager) Login(login *types.Login) soap.HasFault {
Locale: login.Locale,
MessageLocale: login.Locale,
},
Registry: NewRegistry(),
}
ctx.SetSession(session, true)
body.Res = &types.LoginResponse{
Returnval: session.UserSession,
}
}
return body
}
func (s *SessionManager) Logout(*types.Logout) soap.HasFault {
func (s *SessionManager) Logout(ctx *Context, _ *types.Logout) soap.HasFault {
session := ctx.Session
delete(s.sessions, session.Key)
ctx.postEvent(&types.UserLogoutSessionEvent{
IpAddress: session.IpAddress,
UserAgent: session.UserAgent,
SessionId: session.Key,
LoginTime: &session.LoginTime,
})
return &methods.LogoutBody{Res: new(types.LogoutResponse)}
}
func (s *SessionManager) TerminateSession(ctx *Context, req *types.TerminateSession) soap.HasFault {
body := new(methods.TerminateSessionBody)
for _, id := range req.SessionId {
if id == ctx.Session.Key {
body.Fault_ = Fault("", new(types.InvalidArgument))
return body
}
delete(s.sessions, id)
}
body.Res = new(types.TerminateSessionResponse)
return body
}
func (s *SessionManager) AcquireCloneTicket(ctx *Context, _ *types.AcquireCloneTicket) soap.HasFault {
session := *ctx.Session
session.Key = uuid.New().String()
s.sessions[session.Key] = session
return &methods.AcquireCloneTicketBody{
Res: &types.AcquireCloneTicketResponse{
Returnval: session.Key,
},
}
}
func (s *SessionManager) CloneSession(ctx *Context, ticket *types.CloneSession) soap.HasFault {
body := new(methods.CloneSessionBody)
session, exists := s.sessions[ticket.CloneTicket]
if exists {
delete(s.sessions, ticket.CloneTicket) // A clone ticket can only be used once
session.Key = uuid.New().String()
ctx.SetSession(session, true)
body.Res = &types.CloneSessionResponse{
Returnval: session.UserSession,
}
} else {
body.Fault_ = invalidLogin
}
return body
}
func (s *SessionManager) AcquireGenericServiceTicket(ticket *types.AcquireGenericServiceTicket) soap.HasFault {
return &methods.AcquireGenericServiceTicketBody{
Res: &types.AcquireGenericServiceTicketResponse{
@ -81,3 +153,125 @@ func (s *SessionManager) AcquireGenericServiceTicket(ticket *types.AcquireGeneri
},
}
}
// internalContext is the session for use by the in-memory client (Service.RoundTrip)
var internalContext = &Context{
Context: context.Background(),
Session: &Session{
UserSession: types.UserSession{
Key: uuid.New().String(),
},
Registry: NewRegistry(),
},
}
var invalidLogin = Fault("Login failure", new(types.InvalidLogin))
// Context provides per-request Session management.
type Context struct {
req *http.Request
res http.ResponseWriter
m *SessionManager
context.Context
Session *Session
Caller *types.ManagedObjectReference
}
// mapSession maps an HTTP cookie to a Session.
func (c *Context) mapSession() {
if cookie, err := c.req.Cookie(soap.SessionCookieName); err == nil {
if val, ok := c.m.sessions[cookie.Value]; ok {
c.SetSession(val, false)
}
}
}
// SetSession should be called after successful authentication.
func (c *Context) SetSession(session Session, login bool) {
session.UserAgent = c.req.UserAgent()
session.IpAddress = strings.Split(c.req.RemoteAddr, ":")[0]
session.LastActiveTime = time.Now()
c.m.sessions[session.Key] = session
c.Session = &session
if login {
http.SetCookie(c.res, &http.Cookie{
Name: soap.SessionCookieName,
Value: session.Key,
})
c.postEvent(&types.UserLoginSessionEvent{
SessionId: session.Key,
IpAddress: session.IpAddress,
UserAgent: session.UserAgent,
Locale: session.Locale,
})
}
}
// WithLock holds a lock for the given object while then given function is run.
func (c *Context) WithLock(obj mo.Reference, f func()) {
if c.Caller != nil && *c.Caller == obj.Reference() {
// Internal method invocation, obj is already locked
f()
return
}
Map.WithLock(obj, f)
}
// postEvent wraps EventManager.PostEvent for internal use, with a lock on the EventManager.
func (c *Context) postEvent(events ...types.BaseEvent) {
m := Map.EventManager()
c.WithLock(m, func() {
for _, event := range events {
m.PostEvent(c, &types.PostEvent{EventToPost: event})
}
})
}
// Session combines a UserSession and a Registry for per-session managed objects.
type Session struct {
types.UserSession
*Registry
}
// Put wraps Registry.Put, setting the moref value to include the session key.
func (s *Session) Put(item mo.Reference) mo.Reference {
ref := item.Reference()
if ref.Value == "" {
ref.Value = fmt.Sprintf("session[%s]%s", s.Key, uuid.New())
}
s.Registry.setReference(item, ref)
return s.Registry.Put(item)
}
// Get wraps Registry.Get, session-izing singleton objects such as SessionManager and the root PropertyCollector.
func (s *Session) Get(ref types.ManagedObjectReference) mo.Reference {
obj := s.Registry.Get(ref)
if obj != nil {
return obj
}
// Return a session "view" of certain singleton objects
switch ref.Type {
case "SessionManager":
// Clone SessionManager so the PropertyCollector can properly report CurrentSession
m := *Map.SessionManager()
m.CurrentSession = &s.UserSession
// TODO: we could maintain SessionList as part of the SessionManager singleton
for _, session := range m.sessions {
m.SessionList = append(m.SessionList, session.UserSession)
}
return &m
case "PropertyCollector":
if ref == Map.content().PropertyCollector {
return s.Put(NewPropertyCollector(ref))
}
}
return Map.Get(ref)
}

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -59,10 +59,12 @@ type Method struct {
// Service decodes incoming requests and dispatches to a Handler
type Service struct {
client *vim25.Client
sm *SessionManager
readAll func(io.Reader) ([]byte, error)
TLS *tls.Config
TLS *tls.Config
ServeMux *http.ServeMux
}
// Server provides a simulator Service over HTTP
@ -77,6 +79,7 @@ type Server struct {
func New(instance *ServiceInstance) *Service {
s := &Service{
readAll: ioutil.ReadAll,
sm: Map.SessionManager(),
}
s.client, _ = vim25.NewClient(context.Background(), s)
@ -106,8 +109,29 @@ func Fault(msg string, fault types.BaseMethodFault) *soap.Fault {
return f
}
func (s *Service) call(method *Method) soap.HasFault {
func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
handler := Map.Get(method.This)
session := ctx.Session
if session == nil {
switch method.Name {
case "RetrieveServiceContent", "Login", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
// ok for now, TODO: authz
default:
fault := &types.NotAuthenticated{
NoPermission: types.NoPermission{
Object: method.This,
PrivilegeId: "System.View",
},
}
return &serverFaultBody{Reason: Fault("", fault)}
}
} else {
// Prefer the Session.Registry, ServiceContent.PropertyCollector filter field for example is per-session
if h := session.Get(method.This); h != nil {
handler = h
}
}
if handler == nil {
msg := fmt.Sprintf("managed object not found: %s", method.This)
@ -141,7 +165,14 @@ func (s *Service) call(method *Method) soap.HasFault {
}
}
res := m.Call([]reflect.Value{reflect.ValueOf(method.Body)})
var args, res []reflect.Value
if m.Type().NumIn() == 2 {
args = append(args, reflect.ValueOf(ctx))
}
args = append(args, reflect.ValueOf(method.Body))
Map.WithLock(handler, func() {
res = m.Call(args)
})
return res[0].Interface().(soap.HasFault)
}
@ -165,7 +196,10 @@ func (s *Service) RoundTrip(ctx context.Context, request, response soap.HasFault
Body: req.Interface(),
}
res := s.call(method)
res := s.call(&Context{
Context: ctx,
Session: internalContext.Session,
}, method)
if err := res.Fault(); err != nil {
return soap.WrapSoapFault(err)
@ -226,7 +260,11 @@ func (s *Service) About(w http.ResponseWriter, r *http.Request) {
}
seen[m.Name] = true
if m.Type.NumIn() != 2 || m.Type.NumOut() != 1 || m.Type.Out(0) != f {
in := m.Type.NumIn()
if in < 2 || in > 3 { // at least 2 params (receiver and request), optionally a 3rd param (context)
continue
}
if m.Type.NumOut() != 1 || m.Type.Out(0) != f { // all methods return soap.HasFault
continue
}
@ -262,6 +300,15 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(os.Stderr, "Request: %s\n", string(body))
}
ctx := &Context{
req: r,
res: w,
m: s.sm,
Context: context.Background(),
}
Map.WithLock(s.sm, ctx.mapSession)
var res soap.HasFault
var soapBody interface{}
@ -269,7 +316,7 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
if err != nil {
res = serverFault(err.Error())
} else {
res = s.call(method)
res = s.call(ctx, method)
}
if f := res.Fault(); f != nil {
@ -349,20 +396,10 @@ func (s *Service) ServeDatastore(w http.ResponseWriter, r *http.Request) {
return
}
file := strings.TrimPrefix(r.URL.Path, folderPrefix)
p := path.Join(ds.Info.GetDatastoreInfo().Url, file)
r.URL.Path = strings.TrimPrefix(r.URL.Path, folderPrefix)
p := path.Join(ds.Info.GetDatastoreInfo().Url, r.URL.Path)
switch r.Method {
case "GET":
f, err := os.Open(p)
if err != nil {
log.Printf("failed to %s '%s': %s", r.Method, p, err)
w.WriteHeader(http.StatusNotFound)
return
}
defer f.Close()
_, _ = io.Copy(w, f)
case "POST":
_, err := os.Stat(p)
if err == nil {
@ -384,7 +421,9 @@ func (s *Service) ServeDatastore(w http.ResponseWriter, r *http.Request) {
_, _ = io.Copy(f, r.Body)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
fs := http.FileServer(http.Dir(ds.Info.GetDatastoreInfo().Url))
fs.ServeHTTP(w, r)
}
}
@ -408,7 +447,11 @@ func (*Service) ServiceVersions(w http.ResponseWriter, r *http.Request) {
// NewServer returns an http Server instance for the given service
func (s *Service) NewServer() *Server {
mux := http.NewServeMux()
mux := s.ServeMux
if mux == nil {
mux = http.NewServeMux()
}
path := "/sdk"
mux.HandleFunc(path, s.ServeSDK)
@ -428,7 +471,7 @@ func (s *Service) NewServer() *Server {
}
// Redirect clients to this http server, rather than HostSystem.Name
Map.Get(*s.client.ServiceContent.SessionManager).(*SessionManager).ServiceHostName = u.Host
Map.SessionManager().ServiceHostName = u.Host
if f := flag.Lookup("httptest.serve"); f != nil {
// Avoid the blocking behaviour of httptest.Server.Start() when this flag is set
@ -491,7 +534,23 @@ func (s *Server) Close() {
}
}
var typeFunc = types.TypeFunc()
var (
vim25MapType = types.TypeFunc()
typeFunc = defaultMapType
)
func defaultMapType(name string) (reflect.Type, bool) {
typ, ok := vim25MapType(name)
if !ok {
// See TestIssue945, in which case Go does not resolve the namespace and name == "ns1:TraversalSpec"
// Without this hack, the SelectSet would be all nil's
kind := strings.SplitN(name, ":", 2)
if len(kind) == 2 {
typ, ok = vim25MapType(kind[1])
}
}
return typ, ok
}
// UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type
func UnmarshalBody(data []byte) (*Method, error) {

View File

@ -32,13 +32,14 @@ func (v *VirtualMachineSnapshot) RemoveSnapshotTask(req *types.RemoveSnapshot_Ta
Map.Remove(req.This)
vm := Map.Get(v.Vm).(*VirtualMachine)
Map.WithLock(vm, func() {
if vm.Snapshot.CurrentSnapshot != nil && *vm.Snapshot.CurrentSnapshot == req.This {
parent := findParentSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This)
vm.Snapshot.CurrentSnapshot = parent
}
if vm.Snapshot.CurrentSnapshot != nil && *vm.Snapshot.CurrentSnapshot == req.This {
parent := findParentSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This)
vm.Snapshot.CurrentSnapshot = parent
}
vm.Snapshot.RootSnapshotList = removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren)
vm.Snapshot.RootSnapshotList = removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren)
})
return nil, nil
})
@ -54,8 +55,7 @@ func (v *VirtualMachineSnapshot) RevertToSnapshotTask(req *types.RevertToSnapsho
task := CreateTask(v, "revertToSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
vm := Map.Get(v.Vm).(*VirtualMachine)
ref := v.Reference()
vm.Snapshot.CurrentSnapshot = &ref
Map.WithLock(vm, func() { vm.Snapshot.CurrentSnapshot = &v.Self })
return nil, nil
})

View File

@ -50,14 +50,7 @@ func NewUserDirectory(ref types.ManagedObjectReference) object.Reference {
func (u *UserDirectory) RetrieveUserGroups(req *types.RetrieveUserGroups) soap.HasFault {
compare := compareFunc(req.SearchStr, req.ExactMatch)
var res []types.BaseUserSearchResult
for _, ug := range u.userGroup {
if req.FindUsers && !ug.Group || req.FindGroups && ug.Group {
if compare(ug.Principal) {
res = append(res, ug)
}
}
}
res := u.search(req.FindUsers, req.FindGroups, compare)
body := &methods.RetrieveUserGroupsBody{
Res: &types.RetrieveUserGroupsResponse{
@ -68,6 +61,45 @@ func (u *UserDirectory) RetrieveUserGroups(req *types.RetrieveUserGroups) soap.H
return body
}
func (u *UserDirectory) search(findUsers, findGroups bool, compare func(string) bool) (res []types.BaseUserSearchResult) {
for _, ug := range u.userGroup {
if findUsers && !ug.Group || findGroups && ug.Group {
if compare(ug.Principal) {
res = append(res, ug)
}
}
}
return res
}
func (u *UserDirectory) addUser(id string) {
u.add(id, false)
}
func (u *UserDirectory) removeUser(id string) {
u.remove(id, false)
}
func (u *UserDirectory) add(id string, group bool) {
user := &types.UserSearchResult{
FullName: id,
Group: group,
Principal: id,
}
u.userGroup = append(u.userGroup, user)
}
func (u *UserDirectory) remove(id string, group bool) {
for i, ug := range u.userGroup {
if ug.Group == group && ug.Principal == id {
u.userGroup = append(u.userGroup[:i], u.userGroup[i+1:]...)
return
}
}
}
func compareFunc(compared string, exactly bool) func(string) bool {
return func(s string) bool {
if exactly {

View File

@ -71,14 +71,14 @@ func NewViewManager(ref types.ManagedObjectReference) object.Reference {
func destroyView(ref types.ManagedObjectReference) soap.HasFault {
m := Map.ViewManager()
m.ViewList = RemoveReference(ref, m.ViewList)
RemoveReference(&m.ViewList, ref)
return &methods.DestroyViewBody{
Res: &types.DestroyViewResponse{},
}
}
func (m *ViewManager) CreateContainerView(req *types.CreateContainerView) soap.HasFault {
func (m *ViewManager) CreateContainerView(ctx *Context, req *types.CreateContainerView) soap.HasFault {
body := &methods.CreateContainerViewBody{}
root := Map.Get(req.Container)
@ -117,7 +117,7 @@ func (m *ViewManager) CreateContainerView(req *types.CreateContainerView) soap.H
}
}
Map.Put(container)
ctx.Session.Put(container)
m.ViewList = append(m.ViewList, container.Reference())
@ -125,7 +125,8 @@ func (m *ViewManager) CreateContainerView(req *types.CreateContainerView) soap.H
Returnval: container.Self,
}
container.add(root)
seen := make(map[types.ManagedObjectReference]bool)
container.add(root, seen)
return body
}
@ -148,7 +149,7 @@ func (v *ContainerView) include(o types.ManagedObjectReference) bool {
return v.types[o.Type]
}
func (v *ContainerView) add(root mo.Reference) {
func walk(root mo.Reference, f func(child types.ManagedObjectReference)) {
var children []types.ManagedObjectReference
switch e := root.(type) {
@ -173,12 +174,21 @@ func (v *ContainerView) add(root mo.Reference) {
}
for _, child := range children {
f(child)
}
}
func (v *ContainerView) add(root mo.Reference, seen map[types.ManagedObjectReference]bool) {
walk(root, func(child types.ManagedObjectReference) {
if v.include(child) {
v.View = AddReference(child, v.View)
if seen[child] == false {
seen[child] = true
v.View = append(v.View, child)
}
}
if v.Recursive {
v.add(Map.Get(child))
v.add(Map.Get(child), seen)
}
}
})
}

View File

@ -45,7 +45,7 @@ func (m *VirtualDiskManager) names(name string) []string {
}
}
func (m *VirtualDiskManager) createVirtualDisk(req *types.CreateVirtualDisk_Task) types.BaseMethodFault {
func (m *VirtualDiskManager) createVirtualDisk(op types.VirtualDeviceConfigSpecFileOperation, req *types.CreateVirtualDisk_Task) types.BaseMethodFault {
fm := Map.FileManager()
file, fault := fm.resolve(req.Datacenter, req.Name)
@ -53,10 +53,23 @@ func (m *VirtualDiskManager) createVirtualDisk(req *types.CreateVirtualDisk_Task
return fault
}
shouldReplace := op == types.VirtualDeviceConfigSpecFileOperationReplace
shouldExist := op == ""
for _, name := range m.names(file) {
_, err := os.Stat(name)
if err == nil {
if shouldExist {
return nil
}
if shouldReplace {
if err = os.Truncate(file, 0); err != nil {
return fm.fault(name, err, new(types.CannotCreateFile))
}
return nil
}
return fm.fault(name, nil, new(types.FileAlreadyExists))
} else if shouldExist {
return fm.fault(name, nil, new(types.FileNotFound))
}
f, err := os.Create(name)
@ -72,7 +85,7 @@ func (m *VirtualDiskManager) createVirtualDisk(req *types.CreateVirtualDisk_Task
func (m *VirtualDiskManager) CreateVirtualDiskTask(req *types.CreateVirtualDisk_Task) soap.HasFault {
task := CreateTask(m, "createVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
return nil, m.createVirtualDisk(req)
return nil, m.createVirtualDisk(types.VirtualDeviceConfigSpecFileOperationCreate, req)
})
return &methods.CreateVirtualDisk_TaskBody{

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -18,7 +18,6 @@ package simulator
import (
"fmt"
"io"
"log"
"net"
"os"
@ -40,8 +39,7 @@ import (
type VirtualMachine struct {
mo.VirtualMachine
log *log.Logger
out io.Closer
log string
sid int32
}
@ -58,15 +56,22 @@ func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualM
}
rspec := types.DefaultResourceConfigSpec()
vm.Guest = &types.GuestInfo{}
vm.Config = &types.VirtualMachineConfigInfo{
ExtraConfig: []types.BaseOptionValue{&types.OptionValue{Key: "govcsim", Value: "TRUE"}},
Tools: &types.ToolsConfigInfo{},
MemoryAllocation: &rspec.MemoryAllocation,
CpuAllocation: &rspec.CpuAllocation,
}
vm.Snapshot = &types.VirtualMachineSnapshotInfo{}
vm.Storage = &types.VirtualMachineStorageInfo{
Timestamp: time.Now(),
}
vm.Summary.Guest = &types.VirtualMachineGuestSummary{}
vm.Summary.Storage = &types.VirtualMachineStorageSummary{}
vm.Summary.Vm = &vm.Self
vm.Summary.Storage = &types.VirtualMachineStorageSummary{
Timestamp: time.Now(),
}
// Append VM Name as the directory name if not specified
if strings.HasSuffix(spec.Files.VmPathName, "]") { // e.g. "[datastore1]"
@ -84,6 +89,7 @@ func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualM
NumCoresPerSocket: 1,
MemoryMB: 32,
Uuid: uuid.New().String(),
InstanceUuid: uuid.New().String(),
Version: "vmx-11",
Files: &types.VirtualMachineFileInfo{
SnapshotDirectory: dsPath,
@ -111,6 +117,22 @@ func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualM
return vm, nil
}
func (vm *VirtualMachine) event() types.VmEvent {
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
return types.VmEvent{
Event: types.Event{
Datacenter: datacenterEventArgument(host),
ComputeResource: host.eventArgumentParent(),
Host: host.eventArgument(),
Vm: &types.VmEventArgument{
EntityEventArgument: types.EntityEventArgument{Name: vm.Name},
Vm: vm.Self,
},
},
}
}
func (vm *VirtualMachine) apply(spec *types.VirtualMachineConfigSpec) {
if spec.Files == nil {
spec.Files = new(types.VirtualMachineFileInfo)
@ -120,6 +142,12 @@ func (vm *VirtualMachine) apply(spec *types.VirtualMachineConfigSpec) {
src string
dst *string
}{
{spec.AlternateGuestName, &vm.Config.AlternateGuestName},
{spec.Annotation, &vm.Config.Annotation},
{spec.Firmware, &vm.Config.Firmware},
{spec.InstanceUuid, &vm.Config.InstanceUuid},
{spec.LocationId, &vm.Config.LocationId},
{spec.NpivWorldWideNameType, &vm.Config.NpivWorldWideNameType},
{spec.Name, &vm.Name},
{spec.Name, &vm.Config.Name},
{spec.Name, &vm.Summary.Config.Name},
@ -129,6 +157,9 @@ func (vm *VirtualMachine) apply(spec *types.VirtualMachineConfigSpec) {
{spec.GuestId, &vm.Summary.Config.GuestId},
{spec.GuestId, &vm.Summary.Config.GuestFullName},
{spec.Uuid, &vm.Config.Uuid},
{spec.Uuid, &vm.Summary.Config.Uuid},
{spec.InstanceUuid, &vm.Config.InstanceUuid},
{spec.InstanceUuid, &vm.Summary.Config.InstanceUuid},
{spec.Version, &vm.Config.Version},
{spec.Files.VmPathName, &vm.Config.Files.VmPathName},
{spec.Files.VmPathName, &vm.Summary.Config.VmPathName},
@ -142,6 +173,76 @@ func (vm *VirtualMachine) apply(spec *types.VirtualMachineConfigSpec) {
}
}
applyb := []struct {
src *bool
dst **bool
}{
{spec.NestedHVEnabled, &vm.Config.NestedHVEnabled},
{spec.CpuHotAddEnabled, &vm.Config.CpuHotAddEnabled},
{spec.CpuHotRemoveEnabled, &vm.Config.CpuHotRemoveEnabled},
{spec.GuestAutoLockEnabled, &vm.Config.GuestAutoLockEnabled},
{spec.MemoryHotAddEnabled, &vm.Config.MemoryHotAddEnabled},
{spec.MemoryReservationLockedToMax, &vm.Config.MemoryReservationLockedToMax},
{spec.MessageBusTunnelEnabled, &vm.Config.MessageBusTunnelEnabled},
{spec.NpivTemporaryDisabled, &vm.Config.NpivTemporaryDisabled},
{spec.NpivOnNonRdmDisks, &vm.Config.NpivOnNonRdmDisks},
{spec.ChangeTrackingEnabled, &vm.Config.ChangeTrackingEnabled},
}
for _, f := range applyb {
if f.src != nil {
*f.dst = f.src
}
}
if spec.Flags != nil {
vm.Config.Flags = *spec.Flags
}
if spec.LatencySensitivity != nil {
vm.Config.LatencySensitivity = spec.LatencySensitivity
}
if spec.ManagedBy != nil {
vm.Config.ManagedBy = spec.ManagedBy
}
if spec.BootOptions != nil {
vm.Config.BootOptions = spec.BootOptions
}
if spec.RepConfig != nil {
vm.Config.RepConfig = spec.RepConfig
}
if spec.Tools != nil {
vm.Config.Tools = spec.Tools
}
if spec.ConsolePreferences != nil {
vm.Config.ConsolePreferences = spec.ConsolePreferences
}
if spec.CpuAffinity != nil {
vm.Config.CpuAffinity = spec.CpuAffinity
}
if spec.CpuAllocation != nil {
vm.Config.CpuAllocation = spec.CpuAllocation
}
if spec.MemoryAffinity != nil {
vm.Config.MemoryAffinity = spec.MemoryAffinity
}
if spec.MemoryAllocation != nil {
vm.Config.MemoryAllocation = spec.MemoryAllocation
}
if spec.LatencySensitivity != nil {
vm.Config.LatencySensitivity = spec.LatencySensitivity
}
if spec.MemoryMB != 0 {
vm.Config.Hardware.MemoryMB = int32(spec.MemoryMB)
vm.Summary.Config.MemorySizeMB = vm.Config.Hardware.MemoryMB
@ -152,11 +253,13 @@ func (vm *VirtualMachine) apply(spec *types.VirtualMachineConfigSpec) {
vm.Summary.Config.NumCpu = vm.Config.Hardware.NumCPU
}
if spec.NumCoresPerSocket != 0 {
vm.Config.Hardware.NumCoresPerSocket = spec.NumCoresPerSocket
}
vm.Config.ExtraConfig = append(vm.Config.ExtraConfig, spec.ExtraConfig...)
vm.Config.Modified = time.Now()
vm.Summary.Config.Uuid = vm.Config.Uuid
}
func validateGuestID(id string) types.BaseMethodFault {
@ -198,16 +301,13 @@ func (vm *VirtualMachine) useDatastore(name string) *Datastore {
ds := Map.FindByName(name, host.Datastore).(*Datastore)
vm.Datastore = AddReference(ds.Self, vm.Datastore)
if FindReference(vm.Datastore, ds.Self) == nil {
vm.Datastore = append(vm.Datastore, ds.Self)
}
return ds
}
func (vm *VirtualMachine) setLog(w io.WriteCloser) {
vm.out = w
vm.log = log.New(w, "vmx ", log.Flags())
}
func (vm *VirtualMachine) createFile(spec string, name string, register bool) (*os.File, types.BaseMethodFault) {
p, fault := parseDatastorePath(spec)
if fault != nil {
@ -263,17 +363,29 @@ func (vm *VirtualMachine) createFile(spec string, name string, register bool) (*
return f, nil
}
// Rather than keep an fd open for each VM, open/close the log for each messages.
// This is ok for now as we do not do any heavy VM logging.
func (vm *VirtualMachine) logPrintf(format string, v ...interface{}) {
f, err := os.OpenFile(vm.log, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0)
if err != nil {
log.Println(err)
return
}
log.New(f, "vmx ", log.Flags()).Printf(format, v...)
_ = f.Close()
}
func (vm *VirtualMachine) create(spec *types.VirtualMachineConfigSpec, register bool) types.BaseMethodFault {
vm.apply(spec)
files := []struct {
spec string
name string
use func(w io.WriteCloser)
use *string
}{
{vm.Config.Files.VmPathName, "", nil},
{vm.Config.Files.VmPathName, fmt.Sprintf("%s.nvram", vm.Name), nil},
{vm.Config.Files.LogDirectory, "vmware.log", vm.setLog},
{vm.Config.Files.LogDirectory, "vmware.log", &vm.log},
}
for _, file := range files {
@ -281,15 +393,13 @@ func (vm *VirtualMachine) create(spec *types.VirtualMachineConfigSpec, register
if err != nil {
return err
}
if file.use != nil {
file.use(f)
} else {
_ = f.Close()
*file.use = f.Name()
}
_ = f.Close()
}
vm.log.Print("created")
vm.logPrintf("created")
return vm.configureDevices(spec)
}
@ -310,7 +420,8 @@ func (vm *VirtualMachine) generateMAC() string {
return mac.String()
}
func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, device types.BaseVirtualDevice) types.BaseMethodFault {
func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec *types.VirtualDeviceConfigSpec) types.BaseMethodFault {
device := spec.Device
d := device.GetVirtualDevice()
var controller types.BaseVirtualController
@ -371,7 +482,7 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, devi
info.FileName = filename
}
err := dm.createVirtualDisk(&types.CreateVirtualDisk_Task{
err := dm.createVirtualDisk(spec.FileOperation, &types.CreateVirtualDisk_Task{
Datacenter: &dc.Self,
Name: info.FileName,
})
@ -381,10 +492,10 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, devi
p, _ := parseDatastorePath(info.FileName)
info.Datastore = &types.ManagedObjectReference{
Type: "Datastore",
Value: p.Datastore,
}
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
ds := Map.FindByName(p.Datastore, host.Datastore).Reference()
info.Datastore = &ds
}
}
@ -448,6 +559,9 @@ func (vm *VirtualMachine) genVmdkPath() (string, types.BaseMethodFault) {
}
func (vm *VirtualMachine) configureDevices(spec *types.VirtualMachineConfigSpec) types.BaseMethodFault {
dc := Map.getEntityDatacenter(Map.Get(*vm.Parent).(mo.Entity))
dm := Map.VirtualDiskManager()
devices := object.VirtualDeviceList(vm.Config.Hardware.Device)
for i, change := range spec.DeviceChange {
@ -466,7 +580,7 @@ func (vm *VirtualMachine) configureDevices(spec *types.VirtualMachineConfigSpec)
devices = removeDevice(devices, device)
}
err := vm.configureDevice(devices, dspec.Device)
err := vm.configureDevice(devices, dspec)
if err != nil {
return err
}
@ -474,6 +588,23 @@ func (vm *VirtualMachine) configureDevices(spec *types.VirtualMachineConfigSpec)
devices = append(devices, dspec.Device)
case types.VirtualDeviceConfigSpecOperationRemove:
devices = removeDevice(devices, dspec.Device)
disk, ok := dspec.Device.(*types.VirtualDisk)
if ok && dspec.FileOperation == types.VirtualDeviceConfigSpecFileOperationDestroy {
var file string
switch b := disk.Backing.(type) {
case types.BaseVirtualDeviceFileBackingInfo:
file = b.GetVirtualDeviceFileBackingInfo().FileName
}
if file != "" {
dm.DeleteVirtualDiskTask(&types.DeleteVirtualDisk_Task{
Name: file,
Datacenter: &dc.Self,
})
}
}
}
}
@ -486,10 +617,11 @@ type powerVMTask struct {
*VirtualMachine
state types.VirtualMachinePowerState
ctx *Context
}
func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
c.log.Printf("running power task: requesting %s, existing %s",
c.logPrintf("running power task: requesting %s, existing %s",
c.state, c.VirtualMachine.Runtime.PowerState)
if c.VirtualMachine.Runtime.PowerState == c.state {
@ -510,11 +642,28 @@ func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
*bt = nil
}
event := c.event()
switch c.state {
case types.VirtualMachinePowerStatePoweredOn:
c.ctx.postEvent(
&types.VmStartingEvent{VmEvent: event},
&types.VmPoweredOnEvent{VmEvent: event},
)
case types.VirtualMachinePowerStatePoweredOff:
c.ctx.postEvent(&types.VmPoweredOffEvent{VmEvent: event})
}
return nil, nil
}
func (vm *VirtualMachine) PowerOnVMTask(c *types.PowerOnVM_Task) soap.HasFault {
runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOn}
func (vm *VirtualMachine) PowerOnVMTask(ctx *Context, c *types.PowerOnVM_Task) soap.HasFault {
if vm.Config.Template {
return &methods.PowerOnVM_TaskBody{
Fault_: Fault("cannot powerOn a template", &types.InvalidState{}),
}
}
runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOn, ctx}
task := CreateTask(runner.Reference(), "powerOn", runner.Run)
return &methods.PowerOnVM_TaskBody{
@ -524,8 +673,8 @@ func (vm *VirtualMachine) PowerOnVMTask(c *types.PowerOnVM_Task) soap.HasFault {
}
}
func (vm *VirtualMachine) PowerOffVMTask(c *types.PowerOffVM_Task) soap.HasFault {
runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOff}
func (vm *VirtualMachine) PowerOffVMTask(ctx *Context, c *types.PowerOffVM_Task) soap.HasFault {
runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOff, ctx}
task := CreateTask(runner.Reference(), "powerOff", runner.Run)
return &methods.PowerOffVM_TaskBody{
@ -535,13 +684,18 @@ func (vm *VirtualMachine) PowerOffVMTask(c *types.PowerOffVM_Task) soap.HasFault
}
}
func (vm *VirtualMachine) ReconfigVMTask(req *types.ReconfigVM_Task) soap.HasFault {
func (vm *VirtualMachine) ReconfigVMTask(ctx *Context, req *types.ReconfigVM_Task) soap.HasFault {
task := CreateTask(vm, "reconfigVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
err := vm.configure(&req.Spec)
if err != nil {
return nil, err
}
ctx.postEvent(&types.VmReconfiguredEvent{
VmEvent: vm.event(),
ConfigSpec: req.Spec,
})
return nil, nil
})
@ -552,9 +706,9 @@ func (vm *VirtualMachine) ReconfigVMTask(req *types.ReconfigVM_Task) soap.HasFau
}
}
func (vm *VirtualMachine) DestroyTask(req *types.Destroy_Task) soap.HasFault {
func (vm *VirtualMachine) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
task := CreateTask(vm, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
r := vm.UnregisterVM(&types.UnregisterVM{
r := vm.UnregisterVM(ctx, &types.UnregisterVM{
This: req.This,
})
@ -582,7 +736,7 @@ func (vm *VirtualMachine) DestroyTask(req *types.Destroy_Task) soap.HasFault {
}
}
func (vm *VirtualMachine) UnregisterVM(c *types.UnregisterVM) soap.HasFault {
func (vm *VirtualMachine) UnregisterVM(ctx *Context, c *types.UnregisterVM) soap.HasFault {
r := &methods.UnregisterVMBody{}
if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn {
@ -594,34 +748,45 @@ func (vm *VirtualMachine) UnregisterVM(c *types.UnregisterVM) soap.HasFault {
return r
}
_ = vm.out.Close() // Close log fd
Map.getEntityParent(vm, "Folder").(*Folder).removeChild(c.This)
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
host.Vm = RemoveReference(vm.Self, host.Vm)
Map.RemoveReference(host, &host.Vm, vm.Self)
switch pool := Map.Get(*vm.ResourcePool).(type) {
case *ResourcePool:
pool.Vm = RemoveReference(vm.Self, pool.Vm)
Map.RemoveReference(pool, &pool.Vm, vm.Self)
case *VirtualApp:
pool.Vm = RemoveReference(vm.Self, pool.Vm)
Map.RemoveReference(pool, &pool.Vm, vm.Self)
}
for i := range vm.Datastore {
ds := Map.Get(vm.Datastore[i]).(*Datastore)
ds.Vm = RemoveReference(vm.Self, ds.Vm)
Map.RemoveReference(ds, &ds.Vm, vm.Self)
}
ctx.postEvent(&types.VmRemovedEvent{VmEvent: vm.event()})
Map.getEntityParent(vm, "Folder").(*Folder).removeChild(c.This)
r.Res = new(types.UnregisterVMResponse)
return r
}
func (vm *VirtualMachine) CloneVMTask(req *types.CloneVM_Task) soap.HasFault {
task := CreateTask(vm, "cloneVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
folder := Map.Get(req.Folder).(*Folder)
func (vm *VirtualMachine) CloneVMTask(ctx *Context, req *types.CloneVM_Task) soap.HasFault {
ctx.Caller = &vm.Self
folder := Map.Get(req.Folder).(*Folder)
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
event := vm.event()
ctx.postEvent(&types.VmBeingClonedEvent{
VmCloneEvent: types.VmCloneEvent{
VmEvent: event,
},
DestFolder: folder.eventArgument(),
DestName: req.Name,
DestHost: *host.eventArgument(),
})
task := CreateTask(vm, "cloneVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
config := types.VirtualMachineConfigSpec{
Name: req.Name,
GuestId: vm.Config.GuestId,
@ -630,10 +795,11 @@ func (vm *VirtualMachine) CloneVMTask(req *types.CloneVM_Task) soap.HasFault {
},
}
res := folder.CreateVMTask(&types.CreateVM_Task{
res := folder.CreateVMTask(ctx, &types.CreateVM_Task{
This: folder.Self,
Config: config,
Pool: *vm.ResourcePool,
Host: vm.Runtime.Host,
})
ctask := Map.Get(res.(*methods.CreateVM_TaskBody).Res.Returnval).(*Task)
@ -641,7 +807,15 @@ func (vm *VirtualMachine) CloneVMTask(req *types.CloneVM_Task) soap.HasFault {
return nil, ctask.Info.Error.Fault
}
return ctask.Info.Result.(types.ManagedObjectReference), nil
ref := ctask.Info.Result.(types.ManagedObjectReference)
clone := Map.Get(ref).(*VirtualMachine)
ctx.postEvent(&types.VmClonedEvent{
VmCloneEvent: types.VmCloneEvent{VmEvent: clone.event()},
SourceVm: *event.Vm,
})
return ref, nil
})
return &methods.CloneVM_TaskBody{
@ -655,7 +829,7 @@ func (vm *VirtualMachine) RelocateVMTask(req *types.RelocateVM_Task) soap.HasFau
task := CreateTask(vm, "relocateVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
if ref := req.Spec.Datastore; ref != nil {
ds := Map.Get(*ref).(*Datastore)
ds.Vm = RemoveReference(*ref, ds.Vm)
Map.RemoveReference(ds, &ds.Vm, *ref)
vm.Datastore = []types.ManagedObjectReference{*ref}
@ -664,14 +838,14 @@ func (vm *VirtualMachine) RelocateVMTask(req *types.RelocateVM_Task) soap.HasFau
if ref := req.Spec.Pool; ref != nil {
pool := Map.Get(*ref).(*ResourcePool)
pool.Vm = RemoveReference(*ref, pool.Vm)
Map.RemoveReference(pool, &pool.Vm, *ref)
vm.ResourcePool = ref
}
if ref := req.Spec.Host; ref != nil {
host := Map.Get(*ref).(*HostSystem)
host.Vm = RemoveReference(*ref, host.Vm)
Map.RemoveReference(host, &host.Vm, *ref)
vm.Runtime.Host = ref
}
@ -799,6 +973,24 @@ func (vm *VirtualMachine) ShutdownGuest(c *types.ShutdownGuest) soap.HasFault {
return r
}
func (vm *VirtualMachine) MarkAsTemplate(req *types.MarkAsTemplate) soap.HasFault {
r := &methods.MarkAsTemplateBody{}
if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOff {
r.Fault_ = Fault("", &types.InvalidPowerState{
RequestedState: types.VirtualMachinePowerStatePoweredOff,
ExistingState: vm.Runtime.PowerState,
})
return r
}
vm.Config.Template = true
r.Res = &types.MarkAsTemplateResponse{}
return r
}
func findSnapshotInTree(tree []types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference) *types.VirtualMachineSnapshotTree {
if tree == nil {
return nil

View File

@ -57,4 +57,16 @@ var Setting = []types.BaseOptionValue{
Key: "config.vpxd.sso.admin.uri",
Value: "https://127.0.0.1/sso-adminserver/sdk/vsphere.local",
},
&types.OptionValue{
Key: "event.batchsize",
Value: int32(2000),
},
&types.OptionValue{
Key: "event.maxAge",
Value: int32(30),
},
&types.OptionValue{
Key: "event.maxAgeEnabled",
Value: bool(true),
},
}

View File

@ -18,6 +18,7 @@ package progress
import (
"container/list"
"context"
"fmt"
"io"
"sync/atomic"
@ -75,16 +76,16 @@ type reader struct {
pos int64
size int64
bps uint64
bps uint64
ch chan<- Report
ch chan<- Report
ctx context.Context
}
func NewReader(s Sinker, r io.Reader, size int64) *reader {
func NewReader(ctx context.Context, s Sinker, r io.Reader, size int64) *reader {
pr := reader{
r: r,
r: r,
ctx: ctx,
size: size,
}
@ -111,7 +112,10 @@ func (r *reader) Read(b []byte) (int, error) {
bps: &r.bps,
}
r.ch <- q
select {
case r.ch <- q:
case <-r.ctx.Done():
}
return n, err
}
@ -127,8 +131,11 @@ func (r *reader) Done(err error) {
err: err,
}
r.ch <- q
close(r.ch)
select {
case r.ch <- q:
close(r.ch)
case <-r.ctx.Done():
}
}
// newBpsLoop returns a sink that monitors and stores throughput.

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -56,6 +56,7 @@ const (
DefaultVimNamespace = "urn:vim25"
DefaultVimVersion = "6.5"
DefaultMinVimVersion = "5.5"
SessionCookieName = "vmware_soap_session"
)
type header struct {
@ -168,7 +169,7 @@ func (c *Client) NewServiceClient(path string, namespace string) *Client {
// Set SOAP Header cookie
for _, cookie := range client.Jar.Cookies(u) {
if cookie.Name == "vmware_soap_session" {
if cookie.Name == SessionCookieName {
client.cookie = cookie.Value
break
}
@ -458,6 +459,8 @@ func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error
panic(err)
}
req = req.WithContext(ctx)
req.Header.Set(`Content-Type`, `text/xml; charset="utf-8"`)
soapAction := fmt.Sprintf("%s/%s", c.Namespace, c.Version)
req.Header.Set(`SOAPAction`, soapAction)
@ -548,11 +551,11 @@ var DefaultUpload = Upload{
}
// Upload PUTs the local file to the given URL
func (c *Client) Upload(f io.Reader, u *url.URL, param *Upload) error {
func (c *Client) Upload(ctx context.Context, f io.Reader, u *url.URL, param *Upload) error {
var err error
if param.Progress != nil {
pr := progress.NewReader(param.Progress, f, param.ContentLength)
pr := progress.NewReader(ctx, param.Progress, f, param.ContentLength)
f = pr
// Mark progress reader as done when returning from this function.
@ -566,6 +569,8 @@ func (c *Client) Upload(f io.Reader, u *url.URL, param *Upload) error {
return err
}
req = req.WithContext(ctx)
req.ContentLength = param.ContentLength
req.Header.Set("Content-Type", param.Type)
@ -593,7 +598,7 @@ func (c *Client) Upload(f io.Reader, u *url.URL, param *Upload) error {
}
// UploadFile PUTs the local file to the given URL
func (c *Client) UploadFile(file string, u *url.URL, param *Upload) error {
func (c *Client) UploadFile(ctx context.Context, file string, u *url.URL, param *Upload) error {
if param == nil {
p := DefaultUpload // Copy since we set ContentLength
param = &p
@ -612,7 +617,7 @@ func (c *Client) UploadFile(file string, u *url.URL, param *Upload) error {
param.ContentLength = s.Size()
return c.Upload(f, u, param)
return c.Upload(ctx, f, u, param)
}
type Download struct {
@ -628,12 +633,14 @@ var DefaultDownload = Download{
}
// DownloadRequest wraps http.Client.Do, returning the http.Response without checking its StatusCode
func (c *Client) DownloadRequest(u *url.URL, param *Download) (*http.Response, error) {
func (c *Client) DownloadRequest(ctx context.Context, u *url.URL, param *Download) (*http.Response, error) {
req, err := http.NewRequest(param.Method, u.String(), nil)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
for k, v := range param.Headers {
req.Header.Add(k, v)
}
@ -646,8 +653,8 @@ func (c *Client) DownloadRequest(u *url.URL, param *Download) (*http.Response, e
}
// Download GETs the remote file from the given URL
func (c *Client) Download(u *url.URL, param *Download) (io.ReadCloser, int64, error) {
res, err := c.DownloadRequest(u, param)
func (c *Client) Download(ctx context.Context, u *url.URL, param *Download) (io.ReadCloser, int64, error) {
res, err := c.DownloadRequest(ctx, u, param)
if err != nil {
return nil, 0, err
}
@ -667,7 +674,7 @@ func (c *Client) Download(u *url.URL, param *Download) (io.ReadCloser, int64, er
return r, res.ContentLength, nil
}
func (c *Client) WriteFile(file string, src io.Reader, size int64, s progress.Sinker, w io.Writer) error {
func (c *Client) WriteFile(ctx context.Context, file string, src io.Reader, size int64, s progress.Sinker, w io.Writer) error {
var err error
r := src
@ -678,7 +685,7 @@ func (c *Client) WriteFile(file string, src io.Reader, size int64, s progress.Si
}
if s != nil {
pr := progress.NewReader(s, src, size)
pr := progress.NewReader(ctx, s, src, size)
src = pr
// Mark progress reader as done when returning from this function.
@ -705,16 +712,16 @@ func (c *Client) WriteFile(file string, src io.Reader, size int64, s progress.Si
}
// DownloadFile GETs the given URL to a local file
func (c *Client) DownloadFile(file string, u *url.URL, param *Download) error {
func (c *Client) DownloadFile(ctx context.Context, file string, u *url.URL, param *Download) error {
var err error
if param == nil {
param = &DefaultDownload
}
rc, contentLength, err := c.Download(u, param)
rc, contentLength, err := c.Download(ctx, u, param)
if err != nil {
return err
}
return c.WriteFile(file, rc, contentLength, param.Progress, param.Writer)
return c.WriteFile(ctx, file, rc, contentLength, param.Progress, param.Writer)
}

View File

@ -17,6 +17,7 @@ limitations under the License.
package types
import (
"reflect"
"strings"
"time"
)
@ -86,3 +87,9 @@ func DefaultResourceConfigSpec() ResourceConfigSpec {
MemoryAllocation: defaultResourceAllocationInfo(),
}
}
func init() {
// Known 6.5 issue where this event type is sent even though it is internal.
// This workaround allows us to unmarshal and avoid NPEs.
t["HostSubSpecificationUpdateEvent"] = reflect.TypeOf((*HostEvent)(nil)).Elem()
}

View File

@ -48188,7 +48188,7 @@ func init() {
type VirtualMachineAffinityInfo struct {
DynamicData
AffinitySet []int32 `xml:"affinitySet,omitempty"`
AffinitySet []int32 `xml:"affinitySet"`
}
func init() {