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", "ImportPath": "github.com/vmware/govmomi",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/find", "ImportPath": "github.com/vmware/govmomi/find",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/list", "ImportPath": "github.com/vmware/govmomi/list",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/nfc", "ImportPath": "github.com/vmware/govmomi/nfc",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/object", "ImportPath": "github.com/vmware/govmomi/object",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/pbm", "ImportPath": "github.com/vmware/govmomi/pbm",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/pbm/methods", "ImportPath": "github.com/vmware/govmomi/pbm/methods",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/pbm/types", "ImportPath": "github.com/vmware/govmomi/pbm/types",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/property", "ImportPath": "github.com/vmware/govmomi/property",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/session", "ImportPath": "github.com/vmware/govmomi/session",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/simulator", "ImportPath": "github.com/vmware/govmomi/simulator",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/simulator/esx", "ImportPath": "github.com/vmware/govmomi/simulator/esx",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/simulator/vpx", "ImportPath": "github.com/vmware/govmomi/simulator/vpx",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/task", "ImportPath": "github.com/vmware/govmomi/task",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25", "ImportPath": "github.com/vmware/govmomi/vim25",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/debug", "ImportPath": "github.com/vmware/govmomi/vim25/debug",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/methods", "ImportPath": "github.com/vmware/govmomi/vim25/methods",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/mo", "ImportPath": "github.com/vmware/govmomi/vim25/mo",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/progress", "ImportPath": "github.com/vmware/govmomi/vim25/progress",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/soap", "ImportPath": "github.com/vmware/govmomi/vim25/soap",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/types", "ImportPath": "github.com/vmware/govmomi/vim25/types",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi/vim25/xml", "ImportPath": "github.com/vmware/govmomi/vim25/xml",
"Comment": "v0.16.0-5-g5f0f400", "Comment": "v0.16.0-80-g8174315",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17" "Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
}, },
{ {
"ImportPath": "github.com/vmware/photon-controller-go-sdk/SSPI", "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 install -v github.com/vmware/govmomi/vcsim
go-test: go-test:
go test -v $(TEST_OPTS) ./... go test -race -v $(TEST_OPTS) ./...
govc-test: install govc-test: install
(cd govc/test && ./vendor/github.com/sstephenson/bats/libexec/bats -t .) (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 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) { func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*object.ClusterComputeResource, error) {
ccrs, err := f.ClusterComputeResourceList(ctx, path) ccrs, err := f.ClusterComputeResourceList(ctx, path)
if err != nil { if err != nil {
@ -638,6 +647,18 @@ func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*obje
return ccrs[0], nil 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) { func (f *Finder) HostSystemList(ctx context.Context, path string) ([]*object.HostSystem, error) {
s := &spec{ s := &spec{
Relative: f.hostFolder, 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" 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 { 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) 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"
"github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types" "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) { func (c ClusterComputeResource) Configuration(ctx context.Context) (*types.ClusterConfigInfoEx, error) {
req := types.ReconfigureCluster_Task{ var obj mo.ClusterComputeResource
This: c.Reference(),
Spec: spec,
Modify: true,
}
res, err := methods.ReconfigureCluster_Task(ctx, c.c, &req) err := c.Properties(ctx, c.Reference(), []string{"configurationEx"}, &obj)
if err != nil { if err != nil {
return nil, err 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) { 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 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 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 { if err != nil {
return err 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 // 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 { if err != nil {
return err 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 // 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 { if err != nil {
return nil, 0, err 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 // 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 { if err != nil {
return err 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. // AttachedHosts returns hosts that have this Datastore attached, accessible and writable.
@ -406,13 +406,10 @@ func (d Datastore) Stat(ctx context.Context, file string) (types.BaseFileInfo, e
info, err := task.WaitForResult(ctx, nil) info, err := task.WaitForResult(ctx, nil)
if err != nil { if err != nil {
if info == nil || info.Error != nil { if types.IsFileNotFound(err) {
_, ok := info.Error.Fault.(*types.FileNotFound)
if ok {
// FileNotFound means the base path doesn't exist. // FileNotFound means the base path doesn't exist.
return nil, DatastoreNoSuchDirectoryError{"stat", dsPath} return nil, DatastoreNoSuchDirectoryError{"stat", dsPath}
} }
}
return nil, err return nil, err
} }

View File

@ -172,7 +172,7 @@ func (f *DatastoreFile) Stat() (os.FileInfo, error) {
return nil, err return nil, err
} }
res, err := f.d.Client().DownloadRequest(u, p) res, err := f.d.Client().DownloadRequest(f.ctx, u, p)
if err != nil { if err != nil {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }

View File

@ -97,6 +97,15 @@ func (s HostStorageSystem) Refresh(ctx context.Context) error {
return err 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) { func (s HostStorageSystem) MarkAsSsd(ctx context.Context, uuid string) (*Task, error) {
req := types.MarkAsSsd_Task{ req := types.MarkAsSsd_Task{
This: s.Reference(), This: s.Reference(),
@ -152,3 +161,14 @@ func (s HostStorageSystem) MarkAsNonLocal(ctx context.Context, uuid string) (*Ta
return NewTask(s.c, res.Returnval), nil 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 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 // Queries virtual disk uuid
func (m VirtualDiskManager) QueryVirtualDiskUuid(ctx context.Context, name string, dc *Datacenter) (string, error) { func (m VirtualDiskManager) QueryVirtualDiskUuid(ctx context.Context, name string, dc *Datacenter) (string, error) {
req := types.QueryVirtualDiskUuid{ req := types.QueryVirtualDiskUuid{

View File

@ -95,3 +95,67 @@ func (m VirtualDiskManager) QueryVirtualDiskInfo(ctx context.Context, name strin
return info.Result.(arrayOfVirtualDiskInfo).VirtualDiskInfo, nil 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 // of the specified managed objects, with the relevant properties filled in. If
// the properties slice is nil, all properties are loaded. // the properties slice is nil, all properties are loaded.
func (p *Collector) Retrieve(ctx context.Context, objs []types.ManagedObjectReference, ps []string, dst interface{}) error { 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 propSpec *types.PropertySpec
var objectSet []types.ObjectSpec var objectSet []types.ObjectSpec

View File

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

View File

@ -39,7 +39,8 @@ type AuthorizationManager struct {
func NewAuthorizationManager(ref types.ManagedObjectReference) object.Reference { func NewAuthorizationManager(ref types.ManagedObjectReference) object.Reference {
m := &AuthorizationManager{} m := &AuthorizationManager{}
m.Self = ref 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) m.permissions = make(map[types.ManagedObjectReference][]types.Permission)
l := object.AuthorizationRoleList(m.RoleList) l := object.AuthorizationRoleList(m.RoleList)

View File

@ -17,6 +17,9 @@ limitations under the License.
package simulator package simulator
import ( import (
"sync/atomic"
"github.com/google/uuid"
"github.com/vmware/govmomi/simulator/esx" "github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/mo"
@ -26,6 +29,8 @@ import (
type ClusterComputeResource struct { type ClusterComputeResource struct {
mo.ClusterComputeResource mo.ClusterComputeResource
ruleKey int32
} }
type addHost struct { type addHost struct {
@ -44,18 +49,18 @@ func (add *addHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
host := NewHostSystem(esx.HostSystem) host := NewHostSystem(esx.HostSystem)
host.Summary.Config.Name = spec.HostName host.Summary.Config.Name = spec.HostName
host.Name = host.Summary.Config.Name host.Name = host.Summary.Config.Name
if add.req.AsConnected {
host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
} else {
host.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected host.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected
}
cr := add.ClusterComputeResource cr := add.ClusterComputeResource
Map.PutEntity(cr, Map.NewEntity(host)) Map.PutEntity(cr, Map.NewEntity(host))
host.Summary.Host = &host.Self
cr.Host = append(cr.Host, host.Reference()) cr.Host = append(cr.Host, host.Reference())
addComputeResource(cr.Summary.GetComputeResourceSummary(), host)
if add.req.AsConnected {
host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
}
addComputeResource(add.ClusterComputeResource.Summary.GetComputeResourceSummary(), host)
return host.Reference(), nil 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) { func CreateClusterComputeResource(f *Folder, name string, spec types.ClusterConfigSpecEx) (*ClusterComputeResource, types.BaseMethodFault) {
if e := Map.FindByName(name, f.ChildEntity); e != nil { if e := Map.FindByName(name, f.ChildEntity); e != nil {
return nil, &types.DuplicateName{ return nil, &types.DuplicateName{
@ -85,6 +308,7 @@ func CreateClusterComputeResource(f *Folder, name string, spec types.ClusterConf
config := &types.ClusterConfigInfoEx{} config := &types.ClusterConfigInfoEx{}
cluster.ConfigurationEx = config cluster.ConfigurationEx = config
config.VmSwapPlacement = string(types.VirtualMachineConfigInfoSwapPlacementTypeVmDirectory)
config.DrsConfig.Enabled = types.NewBool(true) config.DrsConfig.Enabled = types.NewBool(true)
pool := NewResourcePool() pool := NewResourcePool()

View File

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

View File

@ -74,3 +74,14 @@ func createDatacenterFolders(dc *mo.Datacenter, isVC bool) {
net.putChild(network) 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) s.Summary.PortgroupName = append(s.Summary.PortgroupName, pg.Name)
for _, h := range s.Summary.HostMember { for _, h := range s.Summary.HostMember {
pg.Host = AddReference(h, pg.Host) pg.Host = append(pg.Host, h)
host := Map.Get(h).(*HostSystem) 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} return nil, &types.AlreadyExists{Name: host.Name}
} }
host.Network = append(host.Network, s.Self) Map.AppendReference(host, &host.Network, s.Self)
host.Network = append(host.Network, s.Portgroup...) Map.AppendReference(host, &host.Network, s.Portgroup...)
s.Summary.HostMember = append(s.Summary.HostMember, member.Host) s.Summary.HostMember = append(s.Summary.HostMember, member.Host)
for _, ref := range s.Portgroup { for _, ref := range s.Portgroup {
pg := Map.Get(ref).(*DistributedVirtualPortgroup) pg := Map.Get(ref).(*DistributedVirtualPortgroup)
pg.Host = AddReference(member.Host, pg.Host) Map.AddReference(pg, &pg.Host, member.Host)
} }
case types.ConfigSpecOperationRemove: case types.ConfigSpecOperationRemove:
if pg := FindReference(host.Network, s.Portgroup...); pg != nil { 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) Map.RemoveReference(host, &host.Network, s.Self)
s.Summary.HostMember = RemoveReference(s.Self, s.Summary.HostMember) RemoveReference(&s.Summary.HostMember, s.Self)
case types.ConfigSpecOperationEdit: case types.ConfigSpecOperationEdit:
return nil, &types.NotSupported{} return nil, &types.NotSupported{}
} }

View File

@ -6,6 +6,7 @@ go_library(
"authorization_manager.go", "authorization_manager.go",
"datacenter.go", "datacenter.go",
"doc.go", "doc.go",
"event_manager.go",
"host_config_info.go", "host_config_info.go",
"host_firewall_system.go", "host_firewall_system.go",
"host_hardware_info.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 return body
} }
body.Res = new(types.MakeDirectoryResponse)
return body return body
} }

View File

@ -20,7 +20,6 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"path" "path"
"sync"
"github.com/google/uuid" "github.com/google/uuid"
@ -32,12 +31,17 @@ import (
type Folder struct { type Folder struct {
mo.Folder 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 // 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() ref := o.Reference()
if f.Parent == nil { if f.Parent == nil {
@ -53,9 +57,9 @@ func (f *Folder) update(o mo.Reference, u func(types.ManagedObjectReference, []t
switch ref.Type { switch ref.Type {
case "Network", "DistributedVirtualSwitch", "DistributedVirtualPortgroup": case "Network", "DistributedVirtualSwitch", "DistributedVirtualPortgroup":
dc.Network = u(ref, dc.Network) u(dc, &dc.Network, ref)
case "Datastore": 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) { func (f *Folder) putChild(o mo.Entity) {
Map.PutEntity(f, o) Map.PutEntity(f, o)
f.m.Lock() f.ChildEntity = append(f.ChildEntity, o.Reference())
defer f.m.Unlock()
f.ChildEntity = AddReference(o.Reference(), f.ChildEntity) f.update(o, Map.AddReference)
f.update(o, AddReference)
switch e := o.(type) { switch e := o.(type) {
case *mo.Network: case *mo.Network:
@ -90,12 +91,9 @@ func (f *Folder) putChild(o mo.Entity) {
func (f *Folder) removeChild(o mo.Reference) { func (f *Folder) removeChild(o mo.Reference) {
Map.Remove(o.Reference()) Map.Remove(o.Reference())
f.m.Lock() RemoveReference(&f.ChildEntity, o.Reference())
defer f.m.Unlock()
f.ChildEntity = RemoveReference(o.Reference(), f.ChildEntity) f.update(o, Map.RemoveReference)
f.update(o, RemoveReference)
} }
func (f *Folder) hasChildType(kind string) bool { 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) 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{} r := &methods.CreateDatacenterBody{}
if f.hasChildType("Datacenter") && f.hasChildType("Folder") { if f.hasChildType("Datacenter") && f.hasChildType("Folder") {
@ -210,6 +208,15 @@ func (f *Folder) CreateDatacenter(c *types.CreateDatacenter) soap.HasFault {
r.Res = &types.CreateDatacenterResponse{ r.Res = &types.CreateDatacenterResponse{
Returnval: dc.Self, Returnval: dc.Self,
} }
ctx.postEvent(&types.DatacenterCreatedEvent{
DatacenterEvent: types.DatacenterEvent{
Event: types.Event{
Datacenter: datacenterEventArgument(dc),
},
},
Parent: f.eventArgument(),
})
} else { } else {
r.Fault_ = f.typeNotSupported() r.Fault_ = f.typeNotSupported()
} }
@ -240,6 +247,7 @@ func (f *Folder) CreateClusterEx(c *types.CreateClusterEx) soap.HasFault {
type createVM struct { type createVM struct {
*Folder *Folder
ctx *Context
req *types.CreateVM_Task req *types.CreateVM_Task
register bool register bool
@ -291,27 +299,50 @@ func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
c.Folder.putChild(vm) c.Folder.putChild(vm)
host := Map.Get(*vm.Runtime.Host).(*HostSystem) 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 { for i := range vm.Datastore {
ds := Map.Get(vm.Datastore[i]).(*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) { 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: case *ResourcePool:
rp.Vm = append(rp.Vm, vm.Self) rp.Vm = append(rp.Vm, vm.Self)
case *VirtualApp: case *VirtualApp:
rp.Vm = append(rp.Vm, vm.Self) 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 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{ return &methods.CreateVM_TaskBody{
Res: &types.CreateVM_TaskResponse{ 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 { type registerVM struct {
*Folder *Folder
ctx *Context
req *types.RegisterVM_Task req *types.RegisterVM_Task
} }
@ -367,6 +399,7 @@ func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
create := NewTask(&createVM{ create := NewTask(&createVM{
Folder: c.Folder, Folder: c.Folder,
register: true, register: true,
ctx: c.ctx,
req: &types.CreateVM_Task{ req: &types.CreateVM_Task{
This: c.Folder.Reference(), This: c.Folder.Reference(),
Config: types.VirtualMachineConfigSpec{ Config: types.VirtualMachineConfigSpec{
@ -389,10 +422,12 @@ func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
return create.Info.Result, nil 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{ return &methods.RegisterVM_TaskBody{
Res: &types.RegisterVM_TaskResponse{ 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, 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 { if dvs.Summary.ProductInfo == nil {
product := Map.content().About product := Map.content().About
dvs.Summary.ProductInfo = &types.DistributedVirtualSwitchProductSpec{ 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.Datastore = append(dss.Datastore, ds.Self)
dss.Host.Datastore = dss.Datastore dss.Host.Datastore = dss.Datastore
parent := hostParent(dss.Host) parent := hostParent(dss.Host)
parent.Datastore = AddReference(ds.Self, parent.Datastore) Map.AddReference(parent, &parent.Datastore, ds.Self)
browser := &HostDatastoreBrowser{} browser := &HostDatastoreBrowser{}
browser.Datastore = dss.Datastore 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 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 { func hostParent(host *mo.HostSystem) *mo.ComputeResource {
switch parent := Map.Get(*host.Parent).(type) { switch parent := Map.Get(*host.Parent).(type) {
case *mo.ComputeResource: case *mo.ComputeResource:
@ -137,9 +153,15 @@ func CreateStandaloneHost(f *Folder, spec types.HostConnectSpec) (*HostSystem, t
summary := new(types.ComputeResourceSummary) summary := new(types.ComputeResourceSummary)
addComputeResource(summary, host) 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)) Map.PutEntity(cr, Map.NewEntity(host))
host.Summary.Host = &host.Self
Map.PutEntity(cr, Map.NewEntity(pool)) 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 // This is a simple helper for tests running against a simulator, to populate an inventory
// with commonly used models. // with commonly used models.
type Model struct { type Model struct {
Service *Service Service *Service `json:"-"`
ServiceContent types.ServiceContent ServiceContent types.ServiceContent `json:"-"`
RootFolder mo.Folder RootFolder mo.Folder `json:"-"`
// Autostart will power on Model created VMs when true // Autostart will power on Model created VMs when true
Autostart bool Autostart bool `json:"-"`
// Datacenter specifies the number of Datacenter entities to create // Datacenter specifies the number of Datacenter entities to create
Datacenter int Datacenter int
@ -49,13 +49,13 @@ type Model struct {
Portgroup int Portgroup int
// Host specifies the number of standalone HostSystems entities to create per Datacenter // 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 specifies the number of ClusterComputeResource entities to create per Datacenter
Cluster int Cluster int
// ClusterHost specifies the number of HostSystems entities to create within a Cluster // 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 specifies the number of ResourcePool entities to create per Cluster
Pool int Pool int
@ -233,6 +233,7 @@ func (m *Model) Create() error {
cdrom, _ := devices.CreateCdrom(ide.(*types.VirtualIDEController)) cdrom, _ := devices.CreateCdrom(ide.(*types.VirtualIDEController))
disk := devices.CreateDisk(scsi.(types.BaseVirtualController), ds, disk := devices.CreateDisk(scsi.(types.BaseVirtualController), ds,
config.Files.VmPathName+" "+path.Join(name, "disk1.vmdk")) config.Files.VmPathName+" "+path.Join(name, "disk1.vmdk"))
disk.CapacityInKB = 1024
devices = append(devices, scsi, cdrom, disk, &nic) devices = append(devices, scsi, cdrom, disk, &nic)

View File

@ -57,3 +57,42 @@ func (m *OptionManager) QueryOptions(req *types.QueryOptions) soap.HasFault {
return body 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 { func (s *DistributedVirtualPortgroup) DestroyTask(req *types.Destroy_Task) soap.HasFault {
task := CreateTask(s, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) { task := CreateTask(s, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
vswitch := Map.Get(*s.Config.DistributedVirtualSwitch).(*DistributedVirtualSwitch) vswitch := Map.Get(*s.Config.DistributedVirtualSwitch).(*DistributedVirtualSwitch)
for i, pg := range vswitch.Portgroup { Map.RemoveReference(vswitch, &vswitch.Portgroup, s.Reference())
if pg.Reference() == s.Reference() { Map.removeString(vswitch, &vswitch.Summary.PortgroupName, s.Name)
vswitch.Portgroup = append(vswitch.Portgroup[:i], vswitch.Portgroup[i+1:]...)
break
}
}
f := Map.getEntityParent(vswitch, "Folder").(*Folder) f := Map.getEntityParent(vswitch, "Folder").(*Folder)
f.removeChild(s.Reference()) 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 return nil, nil
}) })

View File

@ -43,12 +43,35 @@ func NewPropertyCollector(ref types.ManagedObjectReference) object.Reference {
var errMissingField = errors.New("missing field") var errMissingField = errors.New("missing field")
var errEmptyField = errors.New("empty field") var errEmptyField = errors.New("empty field")
func getObject(ref types.ManagedObjectReference) (reflect.Value, bool) { func getObject(ctx *Context, ref types.ManagedObjectReference) (reflect.Value, bool) {
obj := Map.Get(ref) 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 { if obj == nil {
return reflect.Value{}, false 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() rval := reflect.ValueOf(obj).Elem()
rtype := rval.Type() 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) { func fieldValue(rval reflect.Value, p string) (interface{}, error) {
var value interface{} var value interface{}
fields := strings.Split(p, ".") fields := strings.Split(p, ".")
for i, name := range fields { for i, name := range fields {
@ -170,7 +192,7 @@ func isEmpty(rval reflect.Value) bool {
switch rval.Kind() { switch rval.Kind() {
case reflect.Ptr: case reflect.Ptr:
return rval.IsNil() return rval.IsNil()
case reflect.String, reflect.Slice: case reflect.String:
return rval.Len() == 0 return rval.Len() == 0
} }
@ -200,7 +222,27 @@ type retrieveResult struct {
specs map[string]*types.TraversalSpec 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++ { for i := 0; i < rval.NumField(); i++ {
val := rval.Field(i) val := rval.Field(i)
@ -212,18 +254,15 @@ func (rr *retrieveResult) collectAll(rval reflect.Value, rtype reflect.Type, con
if f.Anonymous { if f.Anonymous {
// recurse into embedded field // recurse into embedded field
rr.collectAll(val, f.Type, content) rr.collectAll(ctx, val, f.Type, content)
continue continue
} }
content.PropSet = append(content.PropSet, types.DynamicProperty{ rr.add(ctx, lcFirst(f.Name), fieldValueInterface(f, val), content)
Name: lcFirst(f.Name),
Val: fieldValueInterface(f, val),
})
} }
} }
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) seen := make(map[string]bool)
for i := range content.PropSet { for i := range content.PropSet {
@ -240,12 +279,7 @@ func (rr *retrieveResult) collectFields(rval reflect.Value, fields []string, con
val, err := fieldValue(rval, name) val, err := fieldValue(rval, name)
if err == nil { if err == nil {
prop := types.DynamicProperty{ rr.add(ctx, name, val, content)
Name: name,
Val: val,
}
content.PropSet = append(content.PropSet, prop)
continue 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] { if rr.collected[ref] {
return return
} }
@ -272,7 +306,7 @@ func (rr *retrieveResult) collect(ref types.ManagedObjectReference) {
Obj: ref, Obj: ref,
} }
rval, ok := getObject(ref) rval, ok := getObject(ctx, ref)
if !ok { if !ok {
// Possible if a test uses Map.Remove instead of Destroy_Task // Possible if a test uses Map.Remove instead of Destroy_Task
log.Printf("object %s no longer exists", ref) log.Printf("object %s no longer exists", ref)
@ -293,11 +327,11 @@ func (rr *retrieveResult) collect(ref types.ManagedObjectReference) {
} }
if isTrue(p.All) { if isTrue(p.All) {
rr.collectAll(rval, rtype, &content) rr.collectAll(ctx, rval, rtype, &content)
continue 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 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 { for _, ss := range s {
ts, ok := ss.(*types.TraversalSpec) ts, ok := ss.(*types.TraversalSpec)
if ok { if ok {
if ts.Name != "" { if ts.Name != "" {
rr.specs[ts.Name] = ts rr.specs[ts.Name] = ts
@ -335,9 +368,9 @@ func (rr *retrieveResult) selectSet(obj reflect.Value, s []types.BaseSelectionSp
*refs = append(*refs, ref) *refs = append(*refs, ref)
} }
rval, ok := getObject(ref) rval, ok := getObject(ctx, ref)
if ok { 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 return err
} }
} }
@ -347,7 +380,7 @@ func (rr *retrieveResult) selectSet(obj reflect.Value, s []types.BaseSelectionSp
return nil 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 var refs []types.ManagedObjectReference
rr := &retrieveResult{ rr := &retrieveResult{
@ -360,8 +393,7 @@ func (pc *PropertyCollector) collect(r *types.RetrievePropertiesEx) (*types.Retr
// Select object references // Select object references
for _, spec := range r.SpecSet { for _, spec := range r.SpecSet {
for _, o := range spec.ObjectSet { for _, o := range spec.ObjectSet {
rval, ok := getObject(o.Obj) rval, ok := getObject(ctx, o.Obj)
if !ok { if !ok {
if isFalse(spec.ReportMissingObjectsInResults) { if isFalse(spec.ReportMissingObjectsInResults) {
return nil, &types.ManagedObjectNotFound{Obj: o.Obj} 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) 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 return nil, err
} }
} }
} }
for _, ref := range refs { for _, ref := range refs {
rr.collect(ref) rr.collect(ctx, ref)
} }
return rr.RetrieveResult, nil 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{} body := &methods.CreateFilterBody{}
filter := &PropertyFilter{pc: pc} filter := &PropertyFilter{pc: pc}
filter.PartialUpdates = c.PartialUpdates filter.PartialUpdates = c.PartialUpdates
filter.Spec = c.Spec 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{ body.Res = &types.CreateFilterResponse{
Returnval: filter.Self, Returnval: filter.Self,
@ -402,37 +434,37 @@ func (pc *PropertyCollector) CreateFilter(c *types.CreateFilter) soap.HasFault {
return body 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{} body := &methods.CreatePropertyCollectorBody{}
cpc := &PropertyCollector{} cpc := &PropertyCollector{}
body.Res = &types.CreatePropertyCollectorResponse{ body.Res = &types.CreatePropertyCollectorResponse{
Returnval: Map.Put(cpc).Reference(), Returnval: ctx.Session.Put(cpc).Reference(),
} }
return body 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{} body := &methods.DestroyPropertyCollectorBody{}
for _, ref := range pc.Filter { for _, ref := range pc.Filter {
filter := Map.Get(ref).(*PropertyFilter) filter := ctx.Session.Get(ref).(*PropertyFilter)
filter.DestroyPropertyFilter(&types.DestroyPropertyFilter{This: ref}) filter.DestroyPropertyFilter(&types.DestroyPropertyFilter{This: ref})
} }
Map.Remove(c.This) ctx.Session.Remove(c.This)
body.Res = &types.DestroyPropertyCollectorResponse{} body.Res = &types.DestroyPropertyCollectorResponse{}
return body 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{} body := &methods.RetrievePropertiesExBody{}
res, fault := pc.collect(r) res, fault := pc.collect(ctx, r)
if fault != nil { if fault != nil {
body.Fault_ = Fault("", fault) 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. // 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{} body := &methods.RetrievePropertiesBody{}
res := pc.RetrievePropertiesEx(&types.RetrievePropertiesEx{ res := pc.RetrievePropertiesEx(ctx, &types.RetrievePropertiesEx{
This: r.This, This: r.This,
SpecSet: r.SpecSet, SpecSet: r.SpecSet,
}) })
@ -469,7 +501,7 @@ func (pc *PropertyCollector) CancelWaitForUpdates(r *types.CancelWaitForUpdates)
return &methods.CancelWaitForUpdatesBody{Res: new(types.CancelWaitForUpdatesResponse)} 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{} body := &methods.WaitForUpdatesExBody{}
// At the moment we need to support Task completion. Handlers can simply set the Task // 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 { for _, ref := range pc.Filter {
filter := Map.Get(ref).(*PropertyFilter) filter := ctx.Session.Get(ref).(*PropertyFilter)
r := &types.RetrievePropertiesEx{} r := &types.RetrievePropertiesEx{}
r.SpecSet = append(r.SpecSet, filter.Spec) r.SpecSet = append(r.SpecSet, filter.Spec)
res, fault := pc.collect(r) res, fault := pc.collect(ctx, r)
if fault != nil { if fault != nil {
body.Fault_ = Fault("", fault) body.Fault_ = Fault("", fault)
return body 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. // 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{} body := &methods.WaitForUpdatesBody{}
res := pc.WaitForUpdatesEx(&types.WaitForUpdatesEx{ res := pc.WaitForUpdatesEx(ctx, &types.WaitForUpdatesEx{
This: r.This, This: r.This,
Version: r.Version, Version: r.Version,
}) })

View File

@ -32,7 +32,7 @@ type PropertyFilter struct {
func (f *PropertyFilter) DestroyPropertyFilter(c *types.DestroyPropertyFilter) soap.HasFault { func (f *PropertyFilter) DestroyPropertyFilter(c *types.DestroyPropertyFilter) soap.HasFault {
body := &methods.DestroyPropertyFilterBody{} body := &methods.DestroyPropertyFilterBody{}
f.pc.Filter = RemoveReference(c.This, f.pc.Filter) RemoveReference(&f.pc.Filter, c.This)
Map.Remove(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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,7 +17,9 @@ limitations under the License.
package simulator package simulator
import ( import (
"encoding/json"
"fmt" "fmt"
"os"
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
@ -55,6 +57,7 @@ type Registry struct {
m sync.Mutex m sync.Mutex
objects map[types.ManagedObjectReference]mo.Reference objects map[types.ManagedObjectReference]mo.Reference
handlers map[types.ManagedObjectReference]RegisterObject handlers map[types.ManagedObjectReference]RegisterObject
locks map[types.ManagedObjectReference]sync.Locker
counter int counter int
} }
@ -63,6 +66,7 @@ func NewRegistry() *Registry {
r := &Registry{ r := &Registry{
objects: make(map[types.ManagedObjectReference]mo.Reference), objects: make(map[types.ManagedObjectReference]mo.Reference),
handlers: make(map[types.ManagedObjectReference]RegisterObject), handlers: make(map[types.ManagedObjectReference]RegisterObject),
locks: make(map[types.ManagedObjectReference]sync.Locker),
} }
return r return r
@ -99,6 +103,11 @@ func (r *Registry) newReference(item mo.Reference) types.ManagedObjectReference
return ref 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. // AddHandler adds a RegisterObject handler to the Registry.
func (r *Registry) AddHandler(h RegisterObject) { func (r *Registry) AddHandler(h RegisterObject) {
r.handlers[h.Reference()] = h r.handlers[h.Reference()] = h
@ -156,8 +165,7 @@ func (r *Registry) Put(item mo.Reference) mo.Reference {
ref := item.Reference() ref := item.Reference()
if ref.Type == "" || ref.Value == "" { if ref.Type == "" || ref.Value == "" {
ref = r.newReference(item) ref = r.newReference(item)
// mo.Reference() returns a value, not a pointer so use reflect to set the Self field r.setReference(item, ref)
reflect.ValueOf(item).Elem().FieldByName("Self").Set(reflect.ValueOf(ref))
} }
if me, ok := item.(mo.Entity); ok { if me, ok := item.(mo.Entity); ok {
@ -186,6 +194,7 @@ func (r *Registry) Remove(item types.ManagedObjectReference) {
delete(r.objects, item) delete(r.objects, item)
delete(r.handlers, item) delete(r.handlers, item)
delete(r.locks, item)
} }
// getEntityParent traverses up the inventory and returns the first object of type kind. // 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 return nil
} }
// RemoveReference returns a slice with ref removed from refs // AppendReference appends the given refs to field.
func RemoveReference(ref types.ManagedObjectReference, refs []types.ManagedObjectReference) []types.ManagedObjectReference { func (r *Registry) AppendReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref ...types.ManagedObjectReference) {
var result []types.ManagedObjectReference r.WithLock(obj, func() {
*field = append(*field, ref...)
})
}
for i, r := range refs { // 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)
}
})
}
// RemoveReference removes ref from the given field.
func RemoveReference(field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
for i, r := range *field {
if r == ref { if r == ref {
result = append(result, refs[i+1:]...) *field = append((*field)[:i], (*field)[i+1:]...)
break break
} }
}
result = append(result, r)
} }
return result // 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)
})
} }
// AddReference returns a slice with ref appended if not already in refs. func (r *Registry) removeString(obj mo.Reference, field *[]string, val string) {
func AddReference(ref types.ManagedObjectReference, refs []types.ManagedObjectReference) []types.ManagedObjectReference { r.WithLock(obj, func() {
if FindReference(refs, ref) == nil { for i, name := range *field {
return append(refs, ref) if name == val {
*field = append((*field)[:i], (*field)[i+1:]...)
break
} }
}
return refs })
} }
func (r *Registry) content() types.ServiceContent { func (r *Registry) content() types.ServiceContent {
@ -322,6 +350,11 @@ func (r *Registry) SearchIndex() *SearchIndex {
return r.Get(r.content().SearchIndex.Reference()).(*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 // FileManager returns the FileManager singleton
func (r *Registry) FileManager() *FileManager { func (r *Registry) FileManager() *FileManager {
return r.Get(r.content().FileManager.Reference()).(*FileManager) return r.Get(r.content().FileManager.Reference()).(*FileManager)
@ -336,3 +369,57 @@ func (r *Registry) VirtualDiskManager() *VirtualDiskManager {
func (r *Registry) ViewManager() *ViewManager { func (r *Registry) ViewManager() *ViewManager {
return r.Get(r.content().ViewManager.Reference()).(*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 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{} body := &methods.CreateChildVM_TaskBody{}
folder := Map.Get(*a.ParentFolder).(*Folder) folder := Map.Get(*a.ParentFolder).(*Folder)
res := folder.CreateVMTask(&types.CreateVM_Task{ res := folder.CreateVMTask(ctx, &types.CreateVM_Task{
This: folder.Self, This: folder.Self,
Config: req.Config, Config: req.Config,
Host: req.Host, Host: req.Host,
@ -286,18 +287,19 @@ func (p *ResourcePool) DestroyTask(req *types.Destroy_Task) soap.HasFault {
parent := &pp.ResourcePool parent := &pp.ResourcePool
// Remove child reference from rp // 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) // 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 // And VMs move to the parent
vms := p.ResourcePool.Vm vms := p.ResourcePool.Vm
for _, vm := range vms { for _, ref := range vms {
Map.Get(vm).(*VirtualMachine).ResourcePool = &parent.Self 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) Map.Remove(req.This)

View File

@ -62,6 +62,7 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service
NewLicenseManager(*s.Content.LicenseManager), NewLicenseManager(*s.Content.LicenseManager),
NewSearchIndex(*s.Content.SearchIndex), NewSearchIndex(*s.Content.SearchIndex),
NewViewManager(*s.Content.ViewManager), NewViewManager(*s.Content.ViewManager),
NewEventManager(*s.Content.EventManager),
NewTaskManager(*s.Content.TaskManager), NewTaskManager(*s.Content.TaskManager),
NewUserDirectory(*s.Content.UserDirectory), NewUserDirectory(*s.Content.UserDirectory),
NewOptionManager(s.Content.Setting, setting), 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)) objects = append(objects, NewIpPoolManager(*s.Content.IpPoolManager))
} }
if s.Content.AccountManager != nil {
objects = append(objects, NewHostLocalAccountManager(*s.Content.AccountManager))
}
for _, o := range objects { for _, o := range objects {
Map.Put(o) 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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,6 +17,10 @@ limitations under the License.
package simulator package simulator
import ( import (
"context"
"fmt"
"net/http"
"strings"
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
@ -33,26 +37,30 @@ type SessionManager struct {
mo.SessionManager mo.SessionManager
ServiceHostName string ServiceHostName string
sessions map[string]Session
} }
func NewSessionManager(ref types.ManagedObjectReference) object.Reference { func NewSessionManager(ref types.ManagedObjectReference) object.Reference {
s := &SessionManager{} s := &SessionManager{
sessions: make(map[string]Session),
}
s.Self = ref s.Self = ref
return s 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{} body := &methods.LoginBody{}
if login.Locale == "" { if login.Locale == "" {
login.Locale = session.Locale login.Locale = session.Locale
} }
if login.UserName == "" || login.Password == "" { if login.UserName == "" || login.Password == "" || ctx.Session != nil {
body.Fault_ = Fault("Login failure", &types.InvalidLogin{}) body.Fault_ = invalidLogin
} else { } else {
body.Res = &types.LoginResponse{ session := Session{
Returnval: types.UserSession{ UserSession: types.UserSession{
Key: uuid.New().String(), Key: uuid.New().String(),
UserName: login.UserName, UserName: login.UserName,
FullName: login.UserName, FullName: login.UserName,
@ -61,16 +69,80 @@ func (s *SessionManager) Login(login *types.Login) soap.HasFault {
Locale: login.Locale, Locale: login.Locale,
MessageLocale: login.Locale, MessageLocale: login.Locale,
}, },
Registry: NewRegistry(),
}
ctx.SetSession(session, true)
body.Res = &types.LoginResponse{
Returnval: session.UserSession,
} }
} }
return body 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)} 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 { func (s *SessionManager) AcquireGenericServiceTicket(ticket *types.AcquireGenericServiceTicket) soap.HasFault {
return &methods.AcquireGenericServiceTicketBody{ return &methods.AcquireGenericServiceTicketBody{
Res: &types.AcquireGenericServiceTicketResponse{ 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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -59,10 +59,12 @@ type Method struct {
// Service decodes incoming requests and dispatches to a Handler // Service decodes incoming requests and dispatches to a Handler
type Service struct { type Service struct {
client *vim25.Client client *vim25.Client
sm *SessionManager
readAll func(io.Reader) ([]byte, error) readAll func(io.Reader) ([]byte, error)
TLS *tls.Config TLS *tls.Config
ServeMux *http.ServeMux
} }
// Server provides a simulator Service over HTTP // Server provides a simulator Service over HTTP
@ -77,6 +79,7 @@ type Server struct {
func New(instance *ServiceInstance) *Service { func New(instance *ServiceInstance) *Service {
s := &Service{ s := &Service{
readAll: ioutil.ReadAll, readAll: ioutil.ReadAll,
sm: Map.SessionManager(),
} }
s.client, _ = vim25.NewClient(context.Background(), s) s.client, _ = vim25.NewClient(context.Background(), s)
@ -106,8 +109,29 @@ func Fault(msg string, fault types.BaseMethodFault) *soap.Fault {
return f 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) 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 { if handler == nil {
msg := fmt.Sprintf("managed object not found: %s", method.This) 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) return res[0].Interface().(soap.HasFault)
} }
@ -165,7 +196,10 @@ func (s *Service) RoundTrip(ctx context.Context, request, response soap.HasFault
Body: req.Interface(), Body: req.Interface(),
} }
res := s.call(method) res := s.call(&Context{
Context: ctx,
Session: internalContext.Session,
}, method)
if err := res.Fault(); err != nil { if err := res.Fault(); err != nil {
return soap.WrapSoapFault(err) return soap.WrapSoapFault(err)
@ -226,7 +260,11 @@ func (s *Service) About(w http.ResponseWriter, r *http.Request) {
} }
seen[m.Name] = true 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 continue
} }
@ -262,6 +300,15 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(os.Stderr, "Request: %s\n", string(body)) 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 res soap.HasFault
var soapBody interface{} var soapBody interface{}
@ -269,7 +316,7 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
res = serverFault(err.Error()) res = serverFault(err.Error())
} else { } else {
res = s.call(method) res = s.call(ctx, method)
} }
if f := res.Fault(); f != nil { if f := res.Fault(); f != nil {
@ -349,20 +396,10 @@ func (s *Service) ServeDatastore(w http.ResponseWriter, r *http.Request) {
return return
} }
file := strings.TrimPrefix(r.URL.Path, folderPrefix) r.URL.Path = strings.TrimPrefix(r.URL.Path, folderPrefix)
p := path.Join(ds.Info.GetDatastoreInfo().Url, file) p := path.Join(ds.Info.GetDatastoreInfo().Url, r.URL.Path)
switch r.Method { 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": case "POST":
_, err := os.Stat(p) _, err := os.Stat(p)
if err == nil { if err == nil {
@ -384,7 +421,9 @@ func (s *Service) ServeDatastore(w http.ResponseWriter, r *http.Request) {
_, _ = io.Copy(f, r.Body) _, _ = io.Copy(f, r.Body)
default: 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 // NewServer returns an http Server instance for the given service
func (s *Service) NewServer() *Server { func (s *Service) NewServer() *Server {
mux := http.NewServeMux() mux := s.ServeMux
if mux == nil {
mux = http.NewServeMux()
}
path := "/sdk" path := "/sdk"
mux.HandleFunc(path, s.ServeSDK) mux.HandleFunc(path, s.ServeSDK)
@ -428,7 +471,7 @@ func (s *Service) NewServer() *Server {
} }
// Redirect clients to this http server, rather than HostSystem.Name // 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 { if f := flag.Lookup("httptest.serve"); f != nil {
// Avoid the blocking behaviour of httptest.Server.Start() when this flag is set // 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 // UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type
func UnmarshalBody(data []byte) (*Method, error) { func UnmarshalBody(data []byte) (*Method, error) {

View File

@ -32,13 +32,14 @@ func (v *VirtualMachineSnapshot) RemoveSnapshotTask(req *types.RemoveSnapshot_Ta
Map.Remove(req.This) Map.Remove(req.This)
vm := Map.Get(v.Vm).(*VirtualMachine) vm := Map.Get(v.Vm).(*VirtualMachine)
Map.WithLock(vm, func() {
if vm.Snapshot.CurrentSnapshot != nil && *vm.Snapshot.CurrentSnapshot == req.This { if vm.Snapshot.CurrentSnapshot != nil && *vm.Snapshot.CurrentSnapshot == req.This {
parent := findParentSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This) parent := findParentSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This)
vm.Snapshot.CurrentSnapshot = parent 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 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) { task := CreateTask(v, "revertToSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
vm := Map.Get(v.Vm).(*VirtualMachine) vm := Map.Get(v.Vm).(*VirtualMachine)
ref := v.Reference() Map.WithLock(vm, func() { vm.Snapshot.CurrentSnapshot = &v.Self })
vm.Snapshot.CurrentSnapshot = &ref
return nil, nil 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 { func (u *UserDirectory) RetrieveUserGroups(req *types.RetrieveUserGroups) soap.HasFault {
compare := compareFunc(req.SearchStr, req.ExactMatch) compare := compareFunc(req.SearchStr, req.ExactMatch)
var res []types.BaseUserSearchResult res := u.search(req.FindUsers, req.FindGroups, compare)
for _, ug := range u.userGroup {
if req.FindUsers && !ug.Group || req.FindGroups && ug.Group {
if compare(ug.Principal) {
res = append(res, ug)
}
}
}
body := &methods.RetrieveUserGroupsBody{ body := &methods.RetrieveUserGroupsBody{
Res: &types.RetrieveUserGroupsResponse{ Res: &types.RetrieveUserGroupsResponse{
@ -68,6 +61,45 @@ func (u *UserDirectory) RetrieveUserGroups(req *types.RetrieveUserGroups) soap.H
return body 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 { func compareFunc(compared string, exactly bool) func(string) bool {
return func(s string) bool { return func(s string) bool {
if exactly { if exactly {

View File

@ -71,14 +71,14 @@ func NewViewManager(ref types.ManagedObjectReference) object.Reference {
func destroyView(ref types.ManagedObjectReference) soap.HasFault { func destroyView(ref types.ManagedObjectReference) soap.HasFault {
m := Map.ViewManager() m := Map.ViewManager()
m.ViewList = RemoveReference(ref, m.ViewList) RemoveReference(&m.ViewList, ref)
return &methods.DestroyViewBody{ return &methods.DestroyViewBody{
Res: &types.DestroyViewResponse{}, 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{} body := &methods.CreateContainerViewBody{}
root := Map.Get(req.Container) 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()) m.ViewList = append(m.ViewList, container.Reference())
@ -125,7 +125,8 @@ func (m *ViewManager) CreateContainerView(req *types.CreateContainerView) soap.H
Returnval: container.Self, Returnval: container.Self,
} }
container.add(root) seen := make(map[types.ManagedObjectReference]bool)
container.add(root, seen)
return body return body
} }
@ -148,7 +149,7 @@ func (v *ContainerView) include(o types.ManagedObjectReference) bool {
return v.types[o.Type] 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 var children []types.ManagedObjectReference
switch e := root.(type) { switch e := root.(type) {
@ -173,12 +174,21 @@ func (v *ContainerView) add(root mo.Reference) {
} }
for _, child := range children { 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) { 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 { 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() fm := Map.FileManager()
file, fault := fm.resolve(req.Datacenter, req.Name) file, fault := fm.resolve(req.Datacenter, req.Name)
@ -53,10 +53,23 @@ func (m *VirtualDiskManager) createVirtualDisk(req *types.CreateVirtualDisk_Task
return fault return fault
} }
shouldReplace := op == types.VirtualDeviceConfigSpecFileOperationReplace
shouldExist := op == ""
for _, name := range m.names(file) { for _, name := range m.names(file) {
_, err := os.Stat(name) _, err := os.Stat(name)
if err == nil { 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)) return fm.fault(name, nil, new(types.FileAlreadyExists))
} else if shouldExist {
return fm.fault(name, nil, new(types.FileNotFound))
} }
f, err := os.Create(name) 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 { func (m *VirtualDiskManager) CreateVirtualDiskTask(req *types.CreateVirtualDisk_Task) soap.HasFault {
task := CreateTask(m, "createVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) { 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{ 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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -18,7 +18,6 @@ package simulator
import ( import (
"fmt" "fmt"
"io"
"log" "log"
"net" "net"
"os" "os"
@ -40,8 +39,7 @@ import (
type VirtualMachine struct { type VirtualMachine struct {
mo.VirtualMachine mo.VirtualMachine
log *log.Logger log string
out io.Closer
sid int32 sid int32
} }
@ -58,15 +56,22 @@ func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualM
} }
rspec := types.DefaultResourceConfigSpec() rspec := types.DefaultResourceConfigSpec()
vm.Guest = &types.GuestInfo{}
vm.Config = &types.VirtualMachineConfigInfo{ vm.Config = &types.VirtualMachineConfigInfo{
ExtraConfig: []types.BaseOptionValue{&types.OptionValue{Key: "govcsim", Value: "TRUE"}}, ExtraConfig: []types.BaseOptionValue{&types.OptionValue{Key: "govcsim", Value: "TRUE"}},
Tools: &types.ToolsConfigInfo{}, Tools: &types.ToolsConfigInfo{},
MemoryAllocation: &rspec.MemoryAllocation, MemoryAllocation: &rspec.MemoryAllocation,
CpuAllocation: &rspec.CpuAllocation, CpuAllocation: &rspec.CpuAllocation,
} }
vm.Snapshot = &types.VirtualMachineSnapshotInfo{}
vm.Storage = &types.VirtualMachineStorageInfo{
Timestamp: time.Now(),
}
vm.Summary.Guest = &types.VirtualMachineGuestSummary{} vm.Summary.Guest = &types.VirtualMachineGuestSummary{}
vm.Summary.Storage = &types.VirtualMachineStorageSummary{}
vm.Summary.Vm = &vm.Self vm.Summary.Vm = &vm.Self
vm.Summary.Storage = &types.VirtualMachineStorageSummary{
Timestamp: time.Now(),
}
// Append VM Name as the directory name if not specified // Append VM Name as the directory name if not specified
if strings.HasSuffix(spec.Files.VmPathName, "]") { // e.g. "[datastore1]" if strings.HasSuffix(spec.Files.VmPathName, "]") { // e.g. "[datastore1]"
@ -84,6 +89,7 @@ func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualM
NumCoresPerSocket: 1, NumCoresPerSocket: 1,
MemoryMB: 32, MemoryMB: 32,
Uuid: uuid.New().String(), Uuid: uuid.New().String(),
InstanceUuid: uuid.New().String(),
Version: "vmx-11", Version: "vmx-11",
Files: &types.VirtualMachineFileInfo{ Files: &types.VirtualMachineFileInfo{
SnapshotDirectory: dsPath, SnapshotDirectory: dsPath,
@ -111,6 +117,22 @@ func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualM
return vm, nil 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) { func (vm *VirtualMachine) apply(spec *types.VirtualMachineConfigSpec) {
if spec.Files == nil { if spec.Files == nil {
spec.Files = new(types.VirtualMachineFileInfo) spec.Files = new(types.VirtualMachineFileInfo)
@ -120,6 +142,12 @@ func (vm *VirtualMachine) apply(spec *types.VirtualMachineConfigSpec) {
src string src string
dst *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.Name},
{spec.Name, &vm.Config.Name}, {spec.Name, &vm.Config.Name},
{spec.Name, &vm.Summary.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.GuestId},
{spec.GuestId, &vm.Summary.Config.GuestFullName}, {spec.GuestId, &vm.Summary.Config.GuestFullName},
{spec.Uuid, &vm.Config.Uuid}, {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.Version, &vm.Config.Version},
{spec.Files.VmPathName, &vm.Config.Files.VmPathName}, {spec.Files.VmPathName, &vm.Config.Files.VmPathName},
{spec.Files.VmPathName, &vm.Summary.Config.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 { if spec.MemoryMB != 0 {
vm.Config.Hardware.MemoryMB = int32(spec.MemoryMB) vm.Config.Hardware.MemoryMB = int32(spec.MemoryMB)
vm.Summary.Config.MemorySizeMB = vm.Config.Hardware.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 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.ExtraConfig = append(vm.Config.ExtraConfig, spec.ExtraConfig...)
vm.Config.Modified = time.Now() vm.Config.Modified = time.Now()
vm.Summary.Config.Uuid = vm.Config.Uuid
} }
func validateGuestID(id string) types.BaseMethodFault { func validateGuestID(id string) types.BaseMethodFault {
@ -198,14 +301,11 @@ func (vm *VirtualMachine) useDatastore(name string) *Datastore {
ds := Map.FindByName(name, host.Datastore).(*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) { return ds
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) { func (vm *VirtualMachine) createFile(spec string, name string, register bool) (*os.File, types.BaseMethodFault) {
@ -263,17 +363,29 @@ func (vm *VirtualMachine) createFile(spec string, name string, register bool) (*
return f, nil 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 { func (vm *VirtualMachine) create(spec *types.VirtualMachineConfigSpec, register bool) types.BaseMethodFault {
vm.apply(spec) vm.apply(spec)
files := []struct { files := []struct {
spec string spec string
name string name string
use func(w io.WriteCloser) use *string
}{ }{
{vm.Config.Files.VmPathName, "", nil}, {vm.Config.Files.VmPathName, "", nil},
{vm.Config.Files.VmPathName, fmt.Sprintf("%s.nvram", vm.Name), 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 { for _, file := range files {
@ -281,15 +393,13 @@ func (vm *VirtualMachine) create(spec *types.VirtualMachineConfigSpec, register
if err != nil { if err != nil {
return err return err
} }
if file.use != nil { if file.use != nil {
file.use(f) *file.use = f.Name()
} else { }
_ = f.Close() _ = f.Close()
} }
}
vm.log.Print("created") vm.logPrintf("created")
return vm.configureDevices(spec) return vm.configureDevices(spec)
} }
@ -310,7 +420,8 @@ func (vm *VirtualMachine) generateMAC() string {
return mac.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() d := device.GetVirtualDevice()
var controller types.BaseVirtualController var controller types.BaseVirtualController
@ -371,7 +482,7 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, devi
info.FileName = filename info.FileName = filename
} }
err := dm.createVirtualDisk(&types.CreateVirtualDisk_Task{ err := dm.createVirtualDisk(spec.FileOperation, &types.CreateVirtualDisk_Task{
Datacenter: &dc.Self, Datacenter: &dc.Self,
Name: info.FileName, Name: info.FileName,
}) })
@ -381,10 +492,10 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, devi
p, _ := parseDatastorePath(info.FileName) p, _ := parseDatastorePath(info.FileName)
info.Datastore = &types.ManagedObjectReference{ host := Map.Get(*vm.Runtime.Host).(*HostSystem)
Type: "Datastore", ds := Map.FindByName(p.Datastore, host.Datastore).Reference()
Value: p.Datastore,
} info.Datastore = &ds
} }
} }
@ -448,6 +559,9 @@ func (vm *VirtualMachine) genVmdkPath() (string, types.BaseMethodFault) {
} }
func (vm *VirtualMachine) configureDevices(spec *types.VirtualMachineConfigSpec) 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) devices := object.VirtualDeviceList(vm.Config.Hardware.Device)
for i, change := range spec.DeviceChange { for i, change := range spec.DeviceChange {
@ -466,7 +580,7 @@ func (vm *VirtualMachine) configureDevices(spec *types.VirtualMachineConfigSpec)
devices = removeDevice(devices, device) devices = removeDevice(devices, device)
} }
err := vm.configureDevice(devices, dspec.Device) err := vm.configureDevice(devices, dspec)
if err != nil { if err != nil {
return err return err
} }
@ -474,6 +588,23 @@ func (vm *VirtualMachine) configureDevices(spec *types.VirtualMachineConfigSpec)
devices = append(devices, dspec.Device) devices = append(devices, dspec.Device)
case types.VirtualDeviceConfigSpecOperationRemove: case types.VirtualDeviceConfigSpecOperationRemove:
devices = removeDevice(devices, dspec.Device) 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 *VirtualMachine
state types.VirtualMachinePowerState state types.VirtualMachinePowerState
ctx *Context
} }
func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) { 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) c.state, c.VirtualMachine.Runtime.PowerState)
if c.VirtualMachine.Runtime.PowerState == c.state { if c.VirtualMachine.Runtime.PowerState == c.state {
@ -510,11 +642,28 @@ func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
*bt = nil *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 return nil, nil
} }
func (vm *VirtualMachine) PowerOnVMTask(c *types.PowerOnVM_Task) soap.HasFault { func (vm *VirtualMachine) PowerOnVMTask(ctx *Context, c *types.PowerOnVM_Task) soap.HasFault {
runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOn} 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) task := CreateTask(runner.Reference(), "powerOn", runner.Run)
return &methods.PowerOnVM_TaskBody{ 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 { func (vm *VirtualMachine) PowerOffVMTask(ctx *Context, c *types.PowerOffVM_Task) soap.HasFault {
runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOff} runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOff, ctx}
task := CreateTask(runner.Reference(), "powerOff", runner.Run) task := CreateTask(runner.Reference(), "powerOff", runner.Run)
return &methods.PowerOffVM_TaskBody{ 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) { task := CreateTask(vm, "reconfigVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
err := vm.configure(&req.Spec) err := vm.configure(&req.Spec)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctx.postEvent(&types.VmReconfiguredEvent{
VmEvent: vm.event(),
ConfigSpec: req.Spec,
})
return nil, nil 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) { 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, 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{} r := &methods.UnregisterVMBody{}
if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn { if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn {
@ -594,34 +748,45 @@ func (vm *VirtualMachine) UnregisterVM(c *types.UnregisterVM) soap.HasFault {
return r return r
} }
_ = vm.out.Close() // Close log fd
Map.getEntityParent(vm, "Folder").(*Folder).removeChild(c.This)
host := Map.Get(*vm.Runtime.Host).(*HostSystem) 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) { switch pool := Map.Get(*vm.ResourcePool).(type) {
case *ResourcePool: case *ResourcePool:
pool.Vm = RemoveReference(vm.Self, pool.Vm) Map.RemoveReference(pool, &pool.Vm, vm.Self)
case *VirtualApp: case *VirtualApp:
pool.Vm = RemoveReference(vm.Self, pool.Vm) Map.RemoveReference(pool, &pool.Vm, vm.Self)
} }
for i := range vm.Datastore { for i := range vm.Datastore {
ds := Map.Get(vm.Datastore[i]).(*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) r.Res = new(types.UnregisterVMResponse)
return r return r
} }
func (vm *VirtualMachine) CloneVMTask(req *types.CloneVM_Task) soap.HasFault { func (vm *VirtualMachine) CloneVMTask(ctx *Context, req *types.CloneVM_Task) soap.HasFault {
task := CreateTask(vm, "cloneVm", func(t *Task) (types.AnyType, types.BaseMethodFault) { ctx.Caller = &vm.Self
folder := Map.Get(req.Folder).(*Folder) 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{ config := types.VirtualMachineConfigSpec{
Name: req.Name, Name: req.Name,
GuestId: vm.Config.GuestId, 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, This: folder.Self,
Config: config, Config: config,
Pool: *vm.ResourcePool, Pool: *vm.ResourcePool,
Host: vm.Runtime.Host,
}) })
ctask := Map.Get(res.(*methods.CreateVM_TaskBody).Res.Returnval).(*Task) 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 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{ 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) { task := CreateTask(vm, "relocateVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
if ref := req.Spec.Datastore; ref != nil { if ref := req.Spec.Datastore; ref != nil {
ds := Map.Get(*ref).(*Datastore) ds := Map.Get(*ref).(*Datastore)
ds.Vm = RemoveReference(*ref, ds.Vm) Map.RemoveReference(ds, &ds.Vm, *ref)
vm.Datastore = []types.ManagedObjectReference{*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 { if ref := req.Spec.Pool; ref != nil {
pool := Map.Get(*ref).(*ResourcePool) pool := Map.Get(*ref).(*ResourcePool)
pool.Vm = RemoveReference(*ref, pool.Vm) Map.RemoveReference(pool, &pool.Vm, *ref)
vm.ResourcePool = ref vm.ResourcePool = ref
} }
if ref := req.Spec.Host; ref != nil { if ref := req.Spec.Host; ref != nil {
host := Map.Get(*ref).(*HostSystem) host := Map.Get(*ref).(*HostSystem)
host.Vm = RemoveReference(*ref, host.Vm) Map.RemoveReference(host, &host.Vm, *ref)
vm.Runtime.Host = ref vm.Runtime.Host = ref
} }
@ -799,6 +973,24 @@ func (vm *VirtualMachine) ShutdownGuest(c *types.ShutdownGuest) soap.HasFault {
return r 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 { func findSnapshotInTree(tree []types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference) *types.VirtualMachineSnapshotTree {
if tree == nil { if tree == nil {
return nil return nil

View File

@ -57,4 +57,16 @@ var Setting = []types.BaseOptionValue{
Key: "config.vpxd.sso.admin.uri", Key: "config.vpxd.sso.admin.uri",
Value: "https://127.0.0.1/sso-adminserver/sdk/vsphere.local", 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 ( import (
"container/list" "container/list"
"context"
"fmt" "fmt"
"io" "io"
"sync/atomic" "sync/atomic"
@ -75,16 +76,16 @@ type reader struct {
pos int64 pos int64
size 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{ pr := reader{
r: r, r: r,
ctx: ctx,
size: size, size: size,
} }
@ -111,7 +112,10 @@ func (r *reader) Read(b []byte) (int, error) {
bps: &r.bps, bps: &r.bps,
} }
r.ch <- q select {
case r.ch <- q:
case <-r.ctx.Done():
}
return n, err return n, err
} }
@ -127,8 +131,11 @@ func (r *reader) Done(err error) {
err: err, err: err,
} }
r.ch <- q select {
case r.ch <- q:
close(r.ch) close(r.ch)
case <-r.ctx.Done():
}
} }
// newBpsLoop returns a sink that monitors and stores throughput. // 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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -56,6 +56,7 @@ const (
DefaultVimNamespace = "urn:vim25" DefaultVimNamespace = "urn:vim25"
DefaultVimVersion = "6.5" DefaultVimVersion = "6.5"
DefaultMinVimVersion = "5.5" DefaultMinVimVersion = "5.5"
SessionCookieName = "vmware_soap_session"
) )
type header struct { type header struct {
@ -168,7 +169,7 @@ func (c *Client) NewServiceClient(path string, namespace string) *Client {
// Set SOAP Header cookie // Set SOAP Header cookie
for _, cookie := range client.Jar.Cookies(u) { for _, cookie := range client.Jar.Cookies(u) {
if cookie.Name == "vmware_soap_session" { if cookie.Name == SessionCookieName {
client.cookie = cookie.Value client.cookie = cookie.Value
break break
} }
@ -458,6 +459,8 @@ func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error
panic(err) panic(err)
} }
req = req.WithContext(ctx)
req.Header.Set(`Content-Type`, `text/xml; charset="utf-8"`) req.Header.Set(`Content-Type`, `text/xml; charset="utf-8"`)
soapAction := fmt.Sprintf("%s/%s", c.Namespace, c.Version) soapAction := fmt.Sprintf("%s/%s", c.Namespace, c.Version)
req.Header.Set(`SOAPAction`, soapAction) req.Header.Set(`SOAPAction`, soapAction)
@ -548,11 +551,11 @@ var DefaultUpload = Upload{
} }
// Upload PUTs the local file to the given URL // 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 var err error
if param.Progress != nil { if param.Progress != nil {
pr := progress.NewReader(param.Progress, f, param.ContentLength) pr := progress.NewReader(ctx, param.Progress, f, param.ContentLength)
f = pr f = pr
// Mark progress reader as done when returning from this function. // 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 return err
} }
req = req.WithContext(ctx)
req.ContentLength = param.ContentLength req.ContentLength = param.ContentLength
req.Header.Set("Content-Type", param.Type) 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 // 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 { if param == nil {
p := DefaultUpload // Copy since we set ContentLength p := DefaultUpload // Copy since we set ContentLength
param = &p param = &p
@ -612,7 +617,7 @@ func (c *Client) UploadFile(file string, u *url.URL, param *Upload) error {
param.ContentLength = s.Size() param.ContentLength = s.Size()
return c.Upload(f, u, param) return c.Upload(ctx, f, u, param)
} }
type Download struct { type Download struct {
@ -628,12 +633,14 @@ var DefaultDownload = Download{
} }
// DownloadRequest wraps http.Client.Do, returning the http.Response without checking its StatusCode // 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) req, err := http.NewRequest(param.Method, u.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req = req.WithContext(ctx)
for k, v := range param.Headers { for k, v := range param.Headers {
req.Header.Add(k, v) 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 // Download GETs the remote file from the given URL
func (c *Client) Download(u *url.URL, param *Download) (io.ReadCloser, int64, error) { func (c *Client) Download(ctx context.Context, u *url.URL, param *Download) (io.ReadCloser, int64, error) {
res, err := c.DownloadRequest(u, param) res, err := c.DownloadRequest(ctx, u, param)
if err != nil { if err != nil {
return nil, 0, err 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 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 var err error
r := src r := src
@ -678,7 +685,7 @@ func (c *Client) WriteFile(file string, src io.Reader, size int64, s progress.Si
} }
if s != nil { if s != nil {
pr := progress.NewReader(s, src, size) pr := progress.NewReader(ctx, s, src, size)
src = pr src = pr
// Mark progress reader as done when returning from this function. // 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 // 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 var err error
if param == nil { if param == nil {
param = &DefaultDownload param = &DefaultDownload
} }
rc, contentLength, err := c.Download(u, param) rc, contentLength, err := c.Download(ctx, u, param)
if err != nil { if err != nil {
return err 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 package types
import ( import (
"reflect"
"strings" "strings"
"time" "time"
) )
@ -86,3 +87,9 @@ func DefaultResourceConfigSpec() ResourceConfigSpec {
MemoryAllocation: defaultResourceAllocationInfo(), 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 { type VirtualMachineAffinityInfo struct {
DynamicData DynamicData
AffinitySet []int32 `xml:"affinitySet,omitempty"` AffinitySet []int32 `xml:"affinitySet"`
} }
func init() { func init() {