Commit 5c27b98c authored by Doug MacEachern's avatar Doug MacEachern

godep: update vmware/govmomi

parent f821a54d
...@@ -2644,113 +2644,113 @@ ...@@ -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",
......
...@@ -18,7 +18,7 @@ install: ...@@ -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 .)
......
...@@ -625,6 +625,15 @@ func (f *Finder) ClusterComputeResourceList(ctx context.Context, path string) ([ ...@@ -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 ...@@ -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,
......
...@@ -224,7 +224,7 @@ func (l *Lease) Upload(ctx context.Context, item FileItem, f io.Reader, opts soa ...@@ -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 ...@@ -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)
} }
...@@ -21,6 +21,7 @@ import ( ...@@ -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 ...@@ -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 ...@@ -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
}
...@@ -109,16 +109,3 @@ func (c ComputeResource) Reconfigure(ctx context.Context, spec types.BaseCompute ...@@ -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
}
...@@ -284,7 +284,7 @@ func (d Datastore) Upload(ctx context.Context, f io.Reader, path string, param * ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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,12 +406,9 @@ func (d Datastore) Stat(ctx context.Context, file string) (types.BaseFileInfo, e ...@@ -406,12 +406,9 @@ 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) // FileNotFound means the base path doesn't exist.
if ok { return nil, DatastoreNoSuchDirectoryError{"stat", dsPath}
// FileNotFound means the base path doesn't exist.
return nil, DatastoreNoSuchDirectoryError{"stat", dsPath}
}
} }
return nil, err return nil, err
......
...@@ -172,7 +172,7 @@ func (f *DatastoreFile) Stat() (os.FileInfo, error) { ...@@ -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) { ...@@ -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
} }
......
...@@ -97,6 +97,15 @@ func (s HostStorageSystem) Refresh(ctx context.Context) error { ...@@ -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 ...@@ -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
}
...@@ -145,6 +145,47 @@ func (m VirtualDiskManager) DeleteVirtualDisk(ctx context.Context, name string, ...@@ -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{
......
...@@ -95,3 +95,67 @@ func (m VirtualDiskManager) QueryVirtualDiskInfo(ctx context.Context, name strin ...@@ -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
}
...@@ -121,6 +121,10 @@ func (p *Collector) RetrieveProperties(ctx context.Context, req types.RetrievePr ...@@ -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
......
...@@ -11,12 +11,14 @@ go_library( ...@@ -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",
......
...@@ -39,7 +39,8 @@ type AuthorizationManager struct { ...@@ -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)
......
...@@ -17,6 +17,9 @@ limitations under the License. ...@@ -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 ( ...@@ -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) { ...@@ -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
host.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected if add.req.AsConnected {
host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
} else {
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 ...@@ -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 ...@@ -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()
......
...@@ -101,9 +101,11 @@ func (c *CustomFieldsManager) SetField(req *types.SetField) soap.HasFault { ...@@ -101,9 +101,11 @@ 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()
entity.CustomValue = append(entity.CustomValue, &types.CustomFieldStringValue{ Map.WithLock(entity, func() {
CustomFieldValue: types.CustomFieldValue{Key: req.Key}, entity.CustomValue = append(entity.CustomValue, &types.CustomFieldStringValue{
Value: req.Value, CustomFieldValue: types.CustomFieldValue{Key: req.Key},
Value: req.Value,
})
}) })
body.Res = &types.SetFieldResponse{} body.Res = &types.SetFieldResponse{}
......
...@@ -74,3 +74,14 @@ func createDatacenterFolders(dc *mo.Datacenter, isVC bool) { ...@@ -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},
}
}
...@@ -67,9 +67,10 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Ta ...@@ -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_ ...@@ -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_ ...@@ -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{}
} }
......
...@@ -6,6 +6,7 @@ go_library( ...@@ -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",
......
/*
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}}",
},
}
/*
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
}
...@@ -160,6 +160,7 @@ func (f *FileManager) MakeDirectory(req *types.MakeDirectory) soap.HasFault { ...@@ -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
} }
......
...@@ -20,7 +20,6 @@ import ( ...@@ -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 ( ...@@ -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 ...@@ -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 { ...@@ -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, AddReference) f.update(o, Map.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) { ...@@ -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 ...@@ -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 { ...@@ -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 { ...@@ -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) { ...@@ -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)
case *ResourcePool: // This can be an internal call from VirtualApp.CreateChildVMTask, where pool is already locked.
rp.Vm = append(rp.Vm, vm.Self) c.ctx.WithLock(pool, func() {
case *VirtualApp: switch rp := pool.(type) {
rp.Vm = append(rp.Vm, vm.Self) case *ResourcePool:
} rp.Vm = append(rp.Vm, vm.Self)
case *VirtualApp:
rp.Vm = append(rp.Vm, vm.Self)
}
})
event := vm.event()
c.ctx.postEvent(
&types.VmBeingCreatedEvent{
VmEvent: event,
ConfigSpec: &c.req.Config,
},
&types.VmInstanceUuidAssignedEvent{
VmEvent: event,
InstanceUuid: vm.Config.InstanceUuid,
},
&types.VmUuidAssignedEvent{
VmEvent: event,
Uuid: vm.Config.Uuid,
},
&types.VmCreatedEvent{
VmEvent: event,
},
)
return vm.Reference(), nil 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 { ...@@ -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) { ...@@ -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) { ...@@ -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 { ...@@ -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{
......
...@@ -72,7 +72,7 @@ func (dss *HostDatastoreSystem) add(ds *Datastore) *soap.Fault { ...@@ -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
......
/*
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{},
}
}
...@@ -71,6 +71,22 @@ func NewHostSystem(host mo.HostSystem) *HostSystem { ...@@ -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 ...@@ -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))
......
...@@ -34,13 +34,13 @@ import ( ...@@ -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 { ...@@ -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 { ...@@ -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)
......
...@@ -57,3 +57,42 @@ func (m *OptionManager) QueryOptions(req *types.QueryOptions) soap.HasFault { ...@@ -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
}
...@@ -53,23 +53,12 @@ func (s *DistributedVirtualPortgroup) ReconfigureDVPortgroupTask(req *types.Reco ...@@ -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
}) })
......
...@@ -32,7 +32,7 @@ type PropertyFilter struct { ...@@ -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)
......
/* /*
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. ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 { ...@@ -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) { ...@@ -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 ...@@ -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
} }
// AddReference returns a slice with ref appended if not already in refs. // RemoveReference removes ref from the given field.
func AddReference(ref types.ManagedObjectReference, refs []types.ManagedObjectReference) []types.ManagedObjectReference { func (r *Registry) RemoveReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
if FindReference(refs, ref) == nil { r.WithLock(obj, func() {
return append(refs, ref) RemoveReference(field, ref)
} })
}
return refs func (r *Registry) removeString(obj mo.Reference, field *[]string, val string) {
r.WithLock(obj, func() {
for i, name := range *field {
if name == val {
*field = append((*field)[:i], (*field)[i+1:]...)
break
}
}
})
} }
func (r *Registry) content() types.ServiceContent { func (r *Registry) content() types.ServiceContent {
...@@ -322,6 +350,11 @@ func (r *Registry) SearchIndex() *SearchIndex { ...@@ -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 { ...@@ -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()
}
...@@ -252,12 +252,13 @@ func (p *ResourcePool) CreateVApp(req *types.CreateVApp) soap.HasFault { ...@@ -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 { ...@@ -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)
......
...@@ -62,6 +62,7 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service ...@@ -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 ...@@ -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)
} }
......
/* /*
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. ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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)
}
/* /*
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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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 { ...@@ -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() { ...@@ -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) {
......
...@@ -32,13 +32,14 @@ func (v *VirtualMachineSnapshot) RemoveSnapshotTask(req *types.RemoveSnapshot_Ta ...@@ -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 {
parent := findParentSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This)
vm.Snapshot.CurrentSnapshot = parent
}
if vm.Snapshot.CurrentSnapshot != nil && *vm.Snapshot.CurrentSnapshot == req.This { vm.Snapshot.RootSnapshotList = removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren)
parent := findParentSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This) })
vm.Snapshot.CurrentSnapshot = parent
}
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 ...@@ -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
}) })
......
...@@ -50,22 +50,54 @@ func NewUserDirectory(ref types.ManagedObjectReference) object.Reference { ...@@ -50,22 +50,54 @@ 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)
body := &methods.RetrieveUserGroupsBody{
Res: &types.RetrieveUserGroupsResponse{
Returnval: res,
},
}
return body
}
func (u *UserDirectory) search(findUsers, findGroups bool, compare func(string) bool) (res []types.BaseUserSearchResult) {
for _, ug := range u.userGroup { for _, ug := range u.userGroup {
if req.FindUsers && !ug.Group || req.FindGroups && ug.Group { if findUsers && !ug.Group || findGroups && ug.Group {
if compare(ug.Principal) { if compare(ug.Principal) {
res = append(res, ug) res = append(res, ug)
} }
} }
} }
body := &methods.RetrieveUserGroupsBody{ return res
Res: &types.RetrieveUserGroupsResponse{ }
Returnval: 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,
} }
return body 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 {
......
...@@ -71,14 +71,14 @@ func NewViewManager(ref types.ManagedObjectReference) object.Reference { ...@@ -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 ...@@ -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 ...@@ -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 { ...@@ -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) { ...@@ -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)
} }
} })
} }
...@@ -45,7 +45,7 @@ func (m *VirtualDiskManager) names(name string) []string { ...@@ -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 ...@@ -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 ...@@ -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{
......
...@@ -57,4 +57,16 @@ var Setting = []types.BaseOptionValue{ ...@@ -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),
},
} }
...@@ -18,6 +18,7 @@ package progress ...@@ -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 { ...@@ -75,16 +76,16 @@ type reader struct {
pos int64 pos int64
size int64 size int64
bps uint64
bps uint64 ch chan<- Report
ctx context.Context
ch chan<- Report
} }
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) { ...@@ -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) { ...@@ -127,8 +131,11 @@ func (r *reader) Done(err error) {
err: err, err: err,
} }
r.ch <- q select {
close(r.ch) case r.ch <- q:
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.
......
/* /*
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 ( ...@@ -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 { ...@@ -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 ...@@ -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{ ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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{ ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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)
} }
...@@ -17,6 +17,7 @@ limitations under the License. ...@@ -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 { ...@@ -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()
}
...@@ -48188,7 +48188,7 @@ func init() { ...@@ -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() {
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment