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

godep: update vmware/govmomi

parent f821a54d
......@@ -2644,113 +2644,113 @@
},
{
"ImportPath": "github.com/vmware/govmomi",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/find",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/list",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/nfc",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/object",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/pbm",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/pbm/methods",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/pbm/types",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/property",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/session",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/simulator",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/simulator/esx",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/simulator/vpx",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/task",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/debug",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/methods",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/mo",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/progress",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/soap",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/types",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/govmomi/vim25/xml",
"Comment": "v0.16.0-5-g5f0f400",
"Rev": "5f0f4004a1f075f29e715f1b956ca0ab4b428f17"
"Comment": "v0.16.0-80-g8174315",
"Rev": "81743157fb5ccf548d6bd5088c25ee6156a359ee"
},
{
"ImportPath": "github.com/vmware/photon-controller-go-sdk/SSPI",
......
......@@ -18,7 +18,7 @@ install:
go install -v github.com/vmware/govmomi/vcsim
go-test:
go test -v $(TEST_OPTS) ./...
go test -race -v $(TEST_OPTS) ./...
govc-test: install
(cd govc/test && ./vendor/github.com/sstephenson/bats/libexec/bats -t .)
......
......@@ -625,6 +625,15 @@ func (f *Finder) ClusterComputeResourceList(ctx context.Context, path string) ([
return ccrs, nil
}
func (f *Finder) DefaultClusterComputeResource(ctx context.Context) (*object.ClusterComputeResource, error) {
cr, err := f.ClusterComputeResource(ctx, "*")
if err != nil {
return nil, toDefaultError(err)
}
return cr, nil
}
func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*object.ClusterComputeResource, error) {
ccrs, err := f.ClusterComputeResourceList(ctx, path)
if err != nil {
......@@ -638,6 +647,18 @@ func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*obje
return ccrs[0], nil
}
func (f *Finder) ClusterComputeResourceOrDefault(ctx context.Context, path string) (*object.ClusterComputeResource, error) {
if path != "" {
cr, err := f.ClusterComputeResource(ctx, path)
if err != nil {
return nil, err
}
return cr, nil
}
return f.DefaultClusterComputeResource(ctx)
}
func (f *Finder) HostSystemList(ctx context.Context, path string) ([]*object.HostSystem, error) {
s := &spec{
Relative: f.hostFolder,
......
......@@ -224,7 +224,7 @@ func (l *Lease) Upload(ctx context.Context, item FileItem, f io.Reader, opts soa
opts.Type = "application/x-vnd.vmware-streamVmdk"
}
return l.c.Upload(f, item.URL, &opts)
return l.c.Upload(ctx, f, item.URL, &opts)
}
func (l *Lease) DownloadFile(ctx context.Context, file string, item FileItem, opts soap.Download) error {
......@@ -234,5 +234,5 @@ func (l *Lease) DownloadFile(ctx context.Context, file string, item FileItem, op
opts.Progress = progress.Tee(item, opts.Progress)
}
return l.c.DownloadFile(file, item.URL, &opts)
return l.c.DownloadFile(ctx, file, item.URL, &opts)
}
......@@ -21,6 +21,7 @@ import (
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
......@@ -34,19 +35,15 @@ func NewClusterComputeResource(c *vim25.Client, ref types.ManagedObjectReference
}
}
func (c ClusterComputeResource) ReconfigureCluster(ctx context.Context, spec types.ClusterConfigSpec) (*Task, error) {
req := types.ReconfigureCluster_Task{
This: c.Reference(),
Spec: spec,
Modify: true,
}
func (c ClusterComputeResource) Configuration(ctx context.Context) (*types.ClusterConfigInfoEx, error) {
var obj mo.ClusterComputeResource
res, err := methods.ReconfigureCluster_Task(ctx, c.c, &req)
err := c.Properties(ctx, c.Reference(), []string{"configurationEx"}, &obj)
if err != nil {
return nil, err
}
return NewTask(c.c, res.Returnval), nil
return obj.ConfigurationEx.(*types.ClusterConfigInfoEx), nil
}
func (c ClusterComputeResource) AddHost(ctx context.Context, spec types.HostConnectSpec, asConnected bool, license *string, resourcePool *types.ManagedObjectReference) (*Task, error) {
......@@ -71,16 +68,3 @@ func (c ClusterComputeResource) AddHost(ctx context.Context, spec types.HostConn
return NewTask(c.c, res.Returnval), nil
}
func (c ClusterComputeResource) Destroy(ctx context.Context) (*Task, error) {
req := types.Destroy_Task{
This: c.Reference(),
}
res, err := methods.Destroy_Task(ctx, c.c, &req)
if err != nil {
return nil, err
}
return NewTask(c.c, res.Returnval), nil
}
......@@ -109,16 +109,3 @@ func (c ComputeResource) Reconfigure(ctx context.Context, spec types.BaseCompute
return NewTask(c.c, res.Returnval), nil
}
func (c ComputeResource) Destroy(ctx context.Context) (*Task, error) {
req := types.Destroy_Task{
This: c.Reference(),
}
res, err := methods.Destroy_Task(ctx, c.c, &req)
if err != nil {
return nil, err
}
return NewTask(c.c, res.Returnval), nil
}
......@@ -284,7 +284,7 @@ func (d Datastore) Upload(ctx context.Context, f io.Reader, path string, param *
if err != nil {
return err
}
return d.Client().Upload(f, u, p)
return d.Client().Upload(ctx, f, u, p)
}
// UploadFile via soap.Upload with an http service ticket
......@@ -293,7 +293,7 @@ func (d Datastore) UploadFile(ctx context.Context, file string, path string, par
if err != nil {
return err
}
return d.Client().UploadFile(file, u, p)
return d.Client().UploadFile(ctx, file, u, p)
}
// Download via soap.Download with an http service ticket
......@@ -302,7 +302,7 @@ func (d Datastore) Download(ctx context.Context, path string, param *soap.Downlo
if err != nil {
return nil, 0, err
}
return d.Client().Download(u, p)
return d.Client().Download(ctx, u, p)
}
// DownloadFile via soap.Download with an http service ticket
......@@ -311,7 +311,7 @@ func (d Datastore) DownloadFile(ctx context.Context, path string, file string, p
if err != nil {
return err
}
return d.Client().DownloadFile(file, u, p)
return d.Client().DownloadFile(ctx, file, u, p)
}
// AttachedHosts returns hosts that have this Datastore attached, accessible and writable.
......@@ -406,12 +406,9 @@ func (d Datastore) Stat(ctx context.Context, file string) (types.BaseFileInfo, e
info, err := task.WaitForResult(ctx, nil)
if err != nil {
if info == nil || info.Error != nil {
_, ok := info.Error.Fault.(*types.FileNotFound)
if ok {
// FileNotFound means the base path doesn't exist.
return nil, DatastoreNoSuchDirectoryError{"stat", dsPath}
}
if types.IsFileNotFound(err) {
// FileNotFound means the base path doesn't exist.
return nil, DatastoreNoSuchDirectoryError{"stat", dsPath}
}
return nil, err
......
......@@ -172,7 +172,7 @@ func (f *DatastoreFile) Stat() (os.FileInfo, error) {
return nil, err
}
res, err := f.d.Client().DownloadRequest(u, p)
res, err := f.d.Client().DownloadRequest(f.ctx, u, p)
if err != nil {
return nil, err
}
......@@ -202,7 +202,7 @@ func (f *DatastoreFile) get() (io.Reader, error) {
}
}
res, err := f.d.Client().DownloadRequest(u, p)
res, err := f.d.Client().DownloadRequest(f.ctx, u, p)
if err != nil {
return nil, err
}
......
......@@ -97,6 +97,15 @@ func (s HostStorageSystem) Refresh(ctx context.Context) error {
return err
}
func (s HostStorageSystem) RescanVmfs(ctx context.Context) error {
req := types.RescanVmfs{
This: s.Reference(),
}
_, err := methods.RescanVmfs(ctx, s.c, &req)
return err
}
func (s HostStorageSystem) MarkAsSsd(ctx context.Context, uuid string) (*Task, error) {
req := types.MarkAsSsd_Task{
This: s.Reference(),
......@@ -152,3 +161,14 @@ func (s HostStorageSystem) MarkAsNonLocal(ctx context.Context, uuid string) (*Ta
return NewTask(s.c, res.Returnval), nil
}
func (s HostStorageSystem) AttachScsiLun(ctx context.Context, uuid string) error {
req := types.AttachScsiLun{
This: s.Reference(),
LunUuid: uuid,
}
_, err := methods.AttachScsiLun(ctx, s.c, &req)
return err
}
......@@ -145,6 +145,47 @@ func (m VirtualDiskManager) DeleteVirtualDisk(ctx context.Context, name string,
return NewTask(m.c, res.Returnval), nil
}
// InflateVirtualDisk inflates a virtual disk.
func (m VirtualDiskManager) InflateVirtualDisk(ctx context.Context, name string, dc *Datacenter) (*Task, error) {
req := types.InflateVirtualDisk_Task{
This: m.Reference(),
Name: name,
}
if dc != nil {
ref := dc.Reference()
req.Datacenter = &ref
}
res, err := methods.InflateVirtualDisk_Task(ctx, m.c, &req)
if err != nil {
return nil, err
}
return NewTask(m.c, res.Returnval), nil
}
// ShrinkVirtualDisk shrinks a virtual disk.
func (m VirtualDiskManager) ShrinkVirtualDisk(ctx context.Context, name string, dc *Datacenter, copy *bool) (*Task, error) {
req := types.ShrinkVirtualDisk_Task{
This: m.Reference(),
Name: name,
Copy: copy,
}
if dc != nil {
ref := dc.Reference()
req.Datacenter = &ref
}
res, err := methods.ShrinkVirtualDisk_Task(ctx, m.c, &req)
if err != nil {
return nil, err
}
return NewTask(m.c, res.Returnval), nil
}
// Queries virtual disk uuid
func (m VirtualDiskManager) QueryVirtualDiskUuid(ctx context.Context, name string, dc *Datacenter) (string, error) {
req := types.QueryVirtualDiskUuid{
......
......@@ -95,3 +95,67 @@ func (m VirtualDiskManager) QueryVirtualDiskInfo(ctx context.Context, name strin
return info.Result.(arrayOfVirtualDiskInfo).VirtualDiskInfo, nil
}
type createChildDiskTaskRequest struct {
This types.ManagedObjectReference `xml:"_this"`
ChildName string `xml:"childName"`
ChildDatacenter *types.ManagedObjectReference `xml:"childDatacenter,omitempty"`
ParentName string `xml:"parentName"`
ParentDatacenter *types.ManagedObjectReference `xml:"parentDatacenter,omitempty"`
IsLinkedClone bool `xml:"isLinkedClone"`
}
type createChildDiskTaskResponse struct {
Returnval types.ManagedObjectReference `xml:"returnval"`
}
type createChildDiskTaskBody struct {
Req *createChildDiskTaskRequest `xml:"urn:internalvim25 CreateChildDisk_Task,omitempty"`
Res *createChildDiskTaskResponse `xml:"urn:vim25 CreateChildDisk_TaskResponse,omitempty"`
InternalRes *createChildDiskTaskResponse `xml:"urn:internalvim25 CreateChildDisk_TaskResponse,omitempty"`
Err *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *createChildDiskTaskBody) Fault() *soap.Fault { return b.Err }
func createChildDiskTask(ctx context.Context, r soap.RoundTripper, req *createChildDiskTaskRequest) (*createChildDiskTaskResponse, error) {
var reqBody, resBody createChildDiskTaskBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
if resBody.Res != nil {
return resBody.Res, nil // vim-version <= 6.5
}
return resBody.InternalRes, nil // vim-version >= 6.7
}
func (m VirtualDiskManager) CreateChildDisk(ctx context.Context, parent string, pdc *Datacenter, name string, dc *Datacenter, linked bool) (*Task, error) {
req := createChildDiskTaskRequest{
This: m.Reference(),
ChildName: name,
ParentName: parent,
IsLinkedClone: linked,
}
if dc != nil {
ref := dc.Reference()
req.ChildDatacenter = &ref
}
if pdc != nil {
ref := pdc.Reference()
req.ParentDatacenter = &ref
}
res, err := createChildDiskTask(ctx, m.Client(), &req)
if err != nil {
return nil, err
}
return NewTask(m.Client(), res.Returnval), nil
}
......@@ -121,6 +121,10 @@ func (p *Collector) RetrieveProperties(ctx context.Context, req types.RetrievePr
// of the specified managed objects, with the relevant properties filled in. If
// the properties slice is nil, all properties are loaded.
func (p *Collector) Retrieve(ctx context.Context, objs []types.ManagedObjectReference, ps []string, dst interface{}) error {
if len(objs) == 0 {
return errors.New("object references is empty")
}
var propSpec *types.PropertySpec
var objectSet []types.ObjectSpec
......
......@@ -11,12 +11,14 @@ go_library(
"doc.go",
"dvs.go",
"entity.go",
"event_manager.go",
"file_manager.go",
"folder.go",
"guest_id.go",
"host_datastore_browser.go",
"host_datastore_system.go",
"host_firewall_system.go",
"host_local_account_manager.go",
"host_network_system.go",
"host_system.go",
"ip_pool_manager.go",
......
......@@ -39,7 +39,8 @@ type AuthorizationManager struct {
func NewAuthorizationManager(ref types.ManagedObjectReference) object.Reference {
m := &AuthorizationManager{}
m.Self = ref
m.RoleList = esx.RoleList
m.RoleList = make([]types.AuthorizationRole, len(esx.RoleList))
copy(m.RoleList, esx.RoleList)
m.permissions = make(map[types.ManagedObjectReference][]types.Permission)
l := object.AuthorizationRoleList(m.RoleList)
......
......@@ -17,6 +17,9 @@ limitations under the License.
package simulator
import (
"sync/atomic"
"github.com/google/uuid"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
......@@ -26,6 +29,8 @@ import (
type ClusterComputeResource struct {
mo.ClusterComputeResource
ruleKey int32
}
type addHost struct {
......@@ -44,18 +49,18 @@ func (add *addHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
host := NewHostSystem(esx.HostSystem)
host.Summary.Config.Name = spec.HostName
host.Name = host.Summary.Config.Name
host.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected
if add.req.AsConnected {
host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
} else {
host.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected
}
cr := add.ClusterComputeResource
Map.PutEntity(cr, Map.NewEntity(host))
host.Summary.Host = &host.Self
cr.Host = append(cr.Host, host.Reference())
if add.req.AsConnected {
host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
}
addComputeResource(add.ClusterComputeResource.Summary.GetComputeResourceSummary(), host)
addComputeResource(cr.Summary.GetComputeResourceSummary(), host)
return host.Reference(), nil
}
......@@ -68,6 +73,224 @@ func (c *ClusterComputeResource) AddHostTask(add *types.AddHost_Task) soap.HasFa
}
}
func (c *ClusterComputeResource) updateRules(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.RulesSpec {
var i int
exists := false
match := func(info types.BaseClusterRuleInfo) bool {
return info.GetClusterRuleInfo().Name == spec.Info.GetClusterRuleInfo().Name
}
if spec.Operation == types.ArrayUpdateOperationRemove {
match = func(rule types.BaseClusterRuleInfo) bool {
return rule.GetClusterRuleInfo().Key == spec.ArrayUpdateSpec.RemoveKey.(int32)
}
}
for i = range cfg.Rule {
if match(cfg.Rule[i].GetClusterRuleInfo()) {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
info := spec.Info.GetClusterRuleInfo()
info.Key = atomic.AddInt32(&c.ruleKey, 1)
info.RuleUuid = uuid.New().String()
cfg.Rule = append(cfg.Rule, spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
cfg.Rule[i] = spec.Info
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.Rule = append(cfg.Rule[:i], cfg.Rule[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) updateGroups(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.GroupSpec {
var i int
exists := false
match := func(info types.BaseClusterGroupInfo) bool {
return info.GetClusterGroupInfo().Name == spec.Info.GetClusterGroupInfo().Name
}
if spec.Operation == types.ArrayUpdateOperationRemove {
match = func(info types.BaseClusterGroupInfo) bool {
return info.GetClusterGroupInfo().Name == spec.ArrayUpdateSpec.RemoveKey.(string)
}
}
for i = range cfg.Group {
if match(cfg.Group[i].GetClusterGroupInfo()) {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
cfg.Group = append(cfg.Group, spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
cfg.Group[i] = spec.Info
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.Group = append(cfg.Group[:i], cfg.Group[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) updateOverridesDAS(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.DasVmConfigSpec {
var i int
var key types.ManagedObjectReference
exists := false
if spec.Operation == types.ArrayUpdateOperationRemove {
key = spec.RemoveKey.(types.ManagedObjectReference)
} else {
key = spec.Info.Key
}
for i = range cfg.DasVmConfig {
if cfg.DasVmConfig[i].Key == key {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
cfg.DasVmConfig = append(cfg.DasVmConfig, *spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
src := spec.Info.DasSettings
if src == nil {
return new(types.InvalidArgument)
}
dst := cfg.DasVmConfig[i].DasSettings
if src.RestartPriority != "" {
dst.RestartPriority = src.RestartPriority
}
if src.RestartPriorityTimeout != 0 {
dst.RestartPriorityTimeout = src.RestartPriorityTimeout
}
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.DasVmConfig = append(cfg.DasVmConfig[:i], cfg.DasVmConfig[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) updateOverridesDRS(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.DrsVmConfigSpec {
var i int
var key types.ManagedObjectReference
exists := false
if spec.Operation == types.ArrayUpdateOperationRemove {
key = spec.RemoveKey.(types.ManagedObjectReference)
} else {
key = spec.Info.Key
}
for i = range cfg.DrsVmConfig {
if cfg.DrsVmConfig[i].Key == key {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
cfg.DrsVmConfig = append(cfg.DrsVmConfig, *spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
if spec.Info.Enabled != nil {
cfg.DrsVmConfig[i].Enabled = spec.Info.Enabled
}
if spec.Info.Behavior != "" {
cfg.DrsVmConfig[i].Behavior = spec.Info.Behavior
}
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.DrsVmConfig = append(cfg.DrsVmConfig[:i], cfg.DrsVmConfig[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) ReconfigureComputeResourceTask(req *types.ReconfigureComputeResource_Task) soap.HasFault {
task := CreateTask(c, "reconfigureCluster", func(*Task) (types.AnyType, types.BaseMethodFault) {
spec, ok := req.Spec.(*types.ClusterConfigSpecEx)
if !ok {
return nil, new(types.InvalidArgument)
}
updates := []func(*types.ClusterConfigInfoEx, *types.ClusterConfigSpecEx) types.BaseMethodFault{
c.updateRules,
c.updateGroups,
c.updateOverridesDAS,
c.updateOverridesDRS,
}
for _, update := range updates {
if err := update(c.ConfigurationEx.(*types.ClusterConfigInfoEx), spec); err != nil {
return nil, err
}
}
return nil, nil
})
return &methods.ReconfigureComputeResource_TaskBody{
Res: &types.ReconfigureComputeResource_TaskResponse{
Returnval: task.Run(),
},
}
}
func CreateClusterComputeResource(f *Folder, name string, spec types.ClusterConfigSpecEx) (*ClusterComputeResource, types.BaseMethodFault) {
if e := Map.FindByName(name, f.ChildEntity); e != nil {
return nil, &types.DuplicateName{
......@@ -85,6 +308,7 @@ func CreateClusterComputeResource(f *Folder, name string, spec types.ClusterConf
config := &types.ClusterConfigInfoEx{}
cluster.ConfigurationEx = config
config.VmSwapPlacement = string(types.VirtualMachineConfigInfoSwapPlacementTypeVmDirectory)
config.DrsConfig.Enabled = types.NewBool(true)
pool := NewResourcePool()
......
......@@ -101,9 +101,11 @@ func (c *CustomFieldsManager) SetField(req *types.SetField) soap.HasFault {
body := &methods.SetFieldBody{}
entity := Map.Get(req.Entity).(mo.Entity).Entity()
entity.CustomValue = append(entity.CustomValue, &types.CustomFieldStringValue{
CustomFieldValue: types.CustomFieldValue{Key: req.Key},
Value: req.Value,
Map.WithLock(entity, func() {
entity.CustomValue = append(entity.CustomValue, &types.CustomFieldStringValue{
CustomFieldValue: types.CustomFieldValue{Key: req.Key},
Value: req.Value,
})
})
body.Res = &types.SetFieldResponse{}
......
......@@ -74,3 +74,14 @@ func createDatacenterFolders(dc *mo.Datacenter, isVC bool) {
net.putChild(network)
}
}
func datacenterEventArgument(obj mo.Entity) *types.DatacenterEventArgument {
dc, ok := obj.(*mo.Datacenter)
if !ok {
dc = Map.getEntityDatacenter(obj)
}
return &types.DatacenterEventArgument{
Datacenter: dc.Self,
EntityEventArgument: types.EntityEventArgument{Name: dc.Name},
}
}
......@@ -67,9 +67,10 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Ta
s.Summary.PortgroupName = append(s.Summary.PortgroupName, pg.Name)
for _, h := range s.Summary.HostMember {
pg.Host = AddReference(h, pg.Host)
pg.Host = append(pg.Host, h)
host := Map.Get(h).(*HostSystem)
host.Network = append(host.Network, pg.Reference())
Map.AppendReference(host, &host.Network, pg.Reference())
}
}
......@@ -101,13 +102,13 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
return nil, &types.AlreadyExists{Name: host.Name}
}
host.Network = append(host.Network, s.Self)
host.Network = append(host.Network, s.Portgroup...)
Map.AppendReference(host, &host.Network, s.Self)
Map.AppendReference(host, &host.Network, s.Portgroup...)
s.Summary.HostMember = append(s.Summary.HostMember, member.Host)
for _, ref := range s.Portgroup {
pg := Map.Get(ref).(*DistributedVirtualPortgroup)
pg.Host = AddReference(member.Host, pg.Host)
Map.AddReference(pg, &pg.Host, member.Host)
}
case types.ConfigSpecOperationRemove:
if pg := FindReference(host.Network, s.Portgroup...); pg != nil {
......@@ -117,8 +118,8 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
}
}
host.Network = RemoveReference(s.Self, host.Network)
s.Summary.HostMember = RemoveReference(s.Self, s.Summary.HostMember)
Map.RemoveReference(host, &host.Network, s.Self)
RemoveReference(&s.Summary.HostMember, s.Self)
case types.ConfigSpecOperationEdit:
return nil, &types.NotSupported{}
}
......
......@@ -6,6 +6,7 @@ go_library(
"authorization_manager.go",
"datacenter.go",
"doc.go",
"event_manager.go",
"host_config_info.go",
"host_firewall_system.go",
"host_hardware_info.go",
......
/*
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 {
return body
}
body.Res = new(types.MakeDirectoryResponse)
return body
}
......
......@@ -20,7 +20,6 @@ import (
"fmt"
"math/rand"
"path"
"sync"
"github.com/google/uuid"
......@@ -32,12 +31,17 @@ import (
type Folder struct {
mo.Folder
}
m sync.Mutex
func (f *Folder) eventArgument() types.FolderEventArgument {
return types.FolderEventArgument{
Folder: f.Self,
EntityEventArgument: types.EntityEventArgument{Name: f.Name},
}
}
// update references when objects are added/removed from a Folder
func (f *Folder) update(o mo.Reference, u func(types.ManagedObjectReference, []types.ManagedObjectReference) []types.ManagedObjectReference) {
func (f *Folder) update(o mo.Reference, u func(mo.Reference, *[]types.ManagedObjectReference, types.ManagedObjectReference)) {
ref := o.Reference()
if f.Parent == nil {
......@@ -53,9 +57,9 @@ func (f *Folder) update(o mo.Reference, u func(types.ManagedObjectReference, []t
switch ref.Type {
case "Network", "DistributedVirtualSwitch", "DistributedVirtualPortgroup":
dc.Network = u(ref, dc.Network)
u(dc, &dc.Network, ref)
case "Datastore":
dc.Datastore = u(ref, dc.Datastore)
u(dc, &dc.Datastore, ref)
}
}
......@@ -70,12 +74,9 @@ func networkSummary(n *mo.Network) *types.NetworkSummary {
func (f *Folder) putChild(o mo.Entity) {
Map.PutEntity(f, o)
f.m.Lock()
defer f.m.Unlock()
f.ChildEntity = AddReference(o.Reference(), f.ChildEntity)
f.ChildEntity = append(f.ChildEntity, o.Reference())
f.update(o, AddReference)
f.update(o, Map.AddReference)
switch e := o.(type) {
case *mo.Network:
......@@ -90,12 +91,9 @@ func (f *Folder) putChild(o mo.Entity) {
func (f *Folder) removeChild(o mo.Reference) {
Map.Remove(o.Reference())
f.m.Lock()
defer f.m.Unlock()
RemoveReference(&f.ChildEntity, o.Reference())
f.ChildEntity = RemoveReference(o.Reference(), f.ChildEntity)
f.update(o, RemoveReference)
f.update(o, Map.RemoveReference)
}
func (f *Folder) hasChildType(kind string) bool {
......@@ -195,7 +193,7 @@ func (p *StoragePod) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFa
return (&Folder{Folder: p.Folder}).MoveIntoFolderTask(c)
}
func (f *Folder) CreateDatacenter(c *types.CreateDatacenter) soap.HasFault {
func (f *Folder) CreateDatacenter(ctx *Context, c *types.CreateDatacenter) soap.HasFault {
r := &methods.CreateDatacenterBody{}
if f.hasChildType("Datacenter") && f.hasChildType("Folder") {
......@@ -210,6 +208,15 @@ func (f *Folder) CreateDatacenter(c *types.CreateDatacenter) soap.HasFault {
r.Res = &types.CreateDatacenterResponse{
Returnval: dc.Self,
}
ctx.postEvent(&types.DatacenterCreatedEvent{
DatacenterEvent: types.DatacenterEvent{
Event: types.Event{
Datacenter: datacenterEventArgument(dc),
},
},
Parent: f.eventArgument(),
})
} else {
r.Fault_ = f.typeNotSupported()
}
......@@ -240,6 +247,7 @@ func (f *Folder) CreateClusterEx(c *types.CreateClusterEx) soap.HasFault {
type createVM struct {
*Folder
ctx *Context
req *types.CreateVM_Task
register bool
......@@ -291,27 +299,50 @@ func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
c.Folder.putChild(vm)
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
host.Vm = append(host.Vm, vm.Self)
Map.AppendReference(host, &host.Vm, vm.Self)
for i := range vm.Datastore {
ds := Map.Get(vm.Datastore[i]).(*Datastore)
ds.Vm = append(ds.Vm, vm.Self)
Map.AppendReference(ds, &ds.Vm, vm.Self)
}
switch rp := Map.Get(*vm.ResourcePool).(type) {
case *ResourcePool:
rp.Vm = append(rp.Vm, vm.Self)
case *VirtualApp:
rp.Vm = append(rp.Vm, vm.Self)
}
pool := Map.Get(*vm.ResourcePool)
// This can be an internal call from VirtualApp.CreateChildVMTask, where pool is already locked.
c.ctx.WithLock(pool, func() {
switch rp := pool.(type) {
case *ResourcePool:
rp.Vm = append(rp.Vm, vm.Self)
case *VirtualApp:
rp.Vm = append(rp.Vm, vm.Self)
}
})
event := vm.event()
c.ctx.postEvent(
&types.VmBeingCreatedEvent{
VmEvent: event,
ConfigSpec: &c.req.Config,
},
&types.VmInstanceUuidAssignedEvent{
VmEvent: event,
InstanceUuid: vm.Config.InstanceUuid,
},
&types.VmUuidAssignedEvent{
VmEvent: event,
Uuid: vm.Config.Uuid,
},
&types.VmCreatedEvent{
VmEvent: event,
},
)
return vm.Reference(), nil
}
func (f *Folder) CreateVMTask(c *types.CreateVM_Task) soap.HasFault {
func (f *Folder) CreateVMTask(ctx *Context, c *types.CreateVM_Task) soap.HasFault {
return &methods.CreateVM_TaskBody{
Res: &types.CreateVM_TaskResponse{
Returnval: NewTask(&createVM{f, c, false}).Run(),
Returnval: NewTask(&createVM{f, ctx, c, false}).Run(),
},
}
}
......@@ -319,6 +350,7 @@ func (f *Folder) CreateVMTask(c *types.CreateVM_Task) soap.HasFault {
type registerVM struct {
*Folder
ctx *Context
req *types.RegisterVM_Task
}
......@@ -367,6 +399,7 @@ func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
create := NewTask(&createVM{
Folder: c.Folder,
register: true,
ctx: c.ctx,
req: &types.CreateVM_Task{
This: c.Folder.Reference(),
Config: types.VirtualMachineConfigSpec{
......@@ -389,10 +422,12 @@ func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
return create.Info.Result, nil
}
func (f *Folder) RegisterVMTask(c *types.RegisterVM_Task) soap.HasFault {
func (f *Folder) RegisterVMTask(ctx *Context, c *types.RegisterVM_Task) soap.HasFault {
ctx.Caller = &f.Self
return &methods.RegisterVM_TaskBody{
Res: &types.RegisterVM_TaskResponse{
Returnval: NewTask(&registerVM{f, c}).Run(),
Returnval: NewTask(&registerVM{f, ctx, c}).Run(),
},
}
}
......@@ -445,6 +480,33 @@ func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault {
Description: spec.Description,
}
configInfo := &types.VMwareDVSConfigInfo{
DVSConfigInfo: types.DVSConfigInfo{
Uuid: dvs.Uuid,
Name: spec.Name,
ConfigVersion: spec.ConfigVersion,
NumStandalonePorts: spec.NumStandalonePorts,
MaxPorts: spec.MaxPorts,
UplinkPortPolicy: spec.UplinkPortPolicy,
UplinkPortgroup: spec.UplinkPortgroup,
DefaultPortConfig: spec.DefaultPortConfig,
ExtensionKey: spec.ExtensionKey,
Description: spec.Description,
Policy: spec.Policy,
VendorSpecificConfig: spec.VendorSpecificConfig,
SwitchIpAddress: spec.SwitchIpAddress,
DefaultProxySwitchMaxNumPorts: spec.DefaultProxySwitchMaxNumPorts,
InfrastructureTrafficResourceConfig: spec.InfrastructureTrafficResourceConfig,
NetworkResourceControlVersion: spec.NetworkResourceControlVersion,
},
}
if spec.Contact != nil {
configInfo.Contact = *spec.Contact
}
dvs.Config = configInfo
if dvs.Summary.ProductInfo == nil {
product := Map.content().About
dvs.Summary.ProductInfo = &types.DistributedVirtualSwitchProductSpec{
......
......@@ -72,7 +72,7 @@ func (dss *HostDatastoreSystem) add(ds *Datastore) *soap.Fault {
dss.Datastore = append(dss.Datastore, ds.Self)
dss.Host.Datastore = dss.Datastore
parent := hostParent(dss.Host)
parent.Datastore = AddReference(ds.Self, parent.Datastore)
Map.AddReference(parent, &parent.Datastore, ds.Self)
browser := &HostDatastoreBrowser{}
browser.Datastore = dss.Datastore
......
/*
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 {
return hs
}
func (h *HostSystem) eventArgument() *types.HostEventArgument {
return &types.HostEventArgument{
Host: h.Self,
EntityEventArgument: types.EntityEventArgument{Name: h.Name},
}
}
func (h *HostSystem) eventArgumentParent() *types.ComputeResourceEventArgument {
parent := hostParent(&h.HostSystem)
return &types.ComputeResourceEventArgument{
ComputeResource: parent.Self,
EntityEventArgument: types.EntityEventArgument{Name: parent.Name},
}
}
func hostParent(host *mo.HostSystem) *mo.ComputeResource {
switch parent := Map.Get(*host.Parent).(type) {
case *mo.ComputeResource:
......@@ -137,9 +153,15 @@ func CreateStandaloneHost(f *Folder, spec types.HostConnectSpec) (*HostSystem, t
summary := new(types.ComputeResourceSummary)
addComputeResource(summary, host)
cr := &mo.ComputeResource{Summary: summary}
cr := &mo.ComputeResource{
ConfigurationEx: &types.ComputeResourceConfigInfo{
VmSwapPlacement: string(types.VirtualMachineConfigInfoSwapPlacementTypeVmDirectory),
},
Summary: summary,
}
Map.PutEntity(cr, Map.NewEntity(host))
host.Summary.Host = &host.Self
Map.PutEntity(cr, Map.NewEntity(pool))
......
......@@ -34,13 +34,13 @@ import (
// This is a simple helper for tests running against a simulator, to populate an inventory
// with commonly used models.
type Model struct {
Service *Service
Service *Service `json:"-"`
ServiceContent types.ServiceContent
RootFolder mo.Folder
ServiceContent types.ServiceContent `json:"-"`
RootFolder mo.Folder `json:"-"`
// Autostart will power on Model created VMs when true
Autostart bool
Autostart bool `json:"-"`
// Datacenter specifies the number of Datacenter entities to create
Datacenter int
......@@ -49,13 +49,13 @@ type Model struct {
Portgroup int
// Host specifies the number of standalone HostSystems entities to create per Datacenter
Host int
Host int `json:",omitempty"`
// Cluster specifies the number of ClusterComputeResource entities to create per Datacenter
Cluster int
// ClusterHost specifies the number of HostSystems entities to create within a Cluster
ClusterHost int
ClusterHost int `json:",omitempty"`
// Pool specifies the number of ResourcePool entities to create per Cluster
Pool int
......@@ -233,6 +233,7 @@ func (m *Model) Create() error {
cdrom, _ := devices.CreateCdrom(ide.(*types.VirtualIDEController))
disk := devices.CreateDisk(scsi.(types.BaseVirtualController), ds,
config.Files.VmPathName+" "+path.Join(name, "disk1.vmdk"))
disk.CapacityInKB = 1024
devices = append(devices, scsi, cdrom, disk, &nic)
......
......@@ -57,3 +57,42 @@ func (m *OptionManager) QueryOptions(req *types.QueryOptions) soap.HasFault {
return body
}
func (m *OptionManager) find(key string) *types.OptionValue {
for _, opt := range m.Setting {
setting := opt.GetOptionValue()
if setting.Key == key {
return setting
}
}
return nil
}
func (m *OptionManager) UpdateOptions(req *types.UpdateOptions) soap.HasFault {
body := new(methods.UpdateOptionsBody)
for _, change := range req.ChangedValue {
setting := change.GetOptionValue()
// We don't currently include the entire list of default settings for ESX and vCenter,
// this prefix is currently used to test the failure path.
// Real vCenter seems to only allow new options if Key has a "config." prefix.
// TODO: consider behaving the same, which would require including 2 long lists of options in vpx.Setting and esx.Setting
if strings.HasPrefix(setting.Key, "ENOENT.") {
body.Fault_ = Fault("", &types.InvalidName{Name: setting.Key})
return body
}
opt := m.find(setting.Key)
if opt != nil {
// This is an existing option.
opt.Value = setting.Value
continue
}
m.Setting = append(m.Setting, change)
}
body.Res = new(types.UpdateOptionsResponse)
return body
}
......@@ -53,23 +53,12 @@ func (s *DistributedVirtualPortgroup) ReconfigureDVPortgroupTask(req *types.Reco
func (s *DistributedVirtualPortgroup) DestroyTask(req *types.Destroy_Task) soap.HasFault {
task := CreateTask(s, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
vswitch := Map.Get(*s.Config.DistributedVirtualSwitch).(*DistributedVirtualSwitch)
for i, pg := range vswitch.Portgroup {
if pg.Reference() == s.Reference() {
vswitch.Portgroup = append(vswitch.Portgroup[:i], vswitch.Portgroup[i+1:]...)
break
}
}
Map.RemoveReference(vswitch, &vswitch.Portgroup, s.Reference())
Map.removeString(vswitch, &vswitch.Summary.PortgroupName, s.Name)
f := Map.getEntityParent(vswitch, "Folder").(*Folder)
f.removeChild(s.Reference())
for i, name := range vswitch.Summary.PortgroupName {
if name == s.Name {
vswitch.Summary.PortgroupName = append(vswitch.Summary.PortgroupName[:i],
vswitch.Summary.PortgroupName[i+1:]...)
}
}
return nil, nil
})
......
......@@ -32,7 +32,7 @@ type PropertyFilter struct {
func (f *PropertyFilter) DestroyPropertyFilter(c *types.DestroyPropertyFilter) soap.HasFault {
body := &methods.DestroyPropertyFilterBody{}
f.pc.Filter = RemoveReference(c.This, f.pc.Filter)
RemoveReference(&f.pc.Filter, c.This)
Map.Remove(c.This)
......
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -17,7 +17,9 @@ limitations under the License.
package simulator
import (
"encoding/json"
"fmt"
"os"
"reflect"
"strings"
"sync"
......@@ -55,6 +57,7 @@ type Registry struct {
m sync.Mutex
objects map[types.ManagedObjectReference]mo.Reference
handlers map[types.ManagedObjectReference]RegisterObject
locks map[types.ManagedObjectReference]sync.Locker
counter int
}
......@@ -63,6 +66,7 @@ func NewRegistry() *Registry {
r := &Registry{
objects: make(map[types.ManagedObjectReference]mo.Reference),
handlers: make(map[types.ManagedObjectReference]RegisterObject),
locks: make(map[types.ManagedObjectReference]sync.Locker),
}
return r
......@@ -99,6 +103,11 @@ func (r *Registry) newReference(item mo.Reference) types.ManagedObjectReference
return ref
}
func (r *Registry) setReference(item mo.Reference, ref types.ManagedObjectReference) {
// mo.Reference() returns a value, not a pointer so use reflect to set the Self field
reflect.ValueOf(item).Elem().FieldByName("Self").Set(reflect.ValueOf(ref))
}
// AddHandler adds a RegisterObject handler to the Registry.
func (r *Registry) AddHandler(h RegisterObject) {
r.handlers[h.Reference()] = h
......@@ -156,8 +165,7 @@ func (r *Registry) Put(item mo.Reference) mo.Reference {
ref := item.Reference()
if ref.Type == "" || ref.Value == "" {
ref = r.newReference(item)
// mo.Reference() returns a value, not a pointer so use reflect to set the Self field
reflect.ValueOf(item).Elem().FieldByName("Self").Set(reflect.ValueOf(ref))
r.setReference(item, ref)
}
if me, ok := item.(mo.Entity); ok {
......@@ -186,6 +194,7 @@ func (r *Registry) Remove(item types.ManagedObjectReference) {
delete(r.objects, item)
delete(r.handlers, item)
delete(r.locks, item)
}
// getEntityParent traverses up the inventory and returns the first object of type kind.
......@@ -278,29 +287,48 @@ func FindReference(refs []types.ManagedObjectReference, match ...types.ManagedOb
return nil
}
// RemoveReference returns a slice with ref removed from refs
func RemoveReference(ref types.ManagedObjectReference, refs []types.ManagedObjectReference) []types.ManagedObjectReference {
var result []types.ManagedObjectReference
// AppendReference appends the given refs to field.
func (r *Registry) AppendReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref ...types.ManagedObjectReference) {
r.WithLock(obj, func() {
*field = append(*field, ref...)
})
}
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 {
result = append(result, refs[i+1:]...)
*field = append((*field)[:i], (*field)[i+1:]...)
break
}
result = append(result, r)
}
return result
}
// AddReference returns a slice with ref appended if not already in refs.
func AddReference(ref types.ManagedObjectReference, refs []types.ManagedObjectReference) []types.ManagedObjectReference {
if FindReference(refs, ref) == nil {
return append(refs, ref)
}
// RemoveReference removes ref from the given field.
func (r *Registry) RemoveReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
r.WithLock(obj, func() {
RemoveReference(field, ref)
})
}
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 {
......@@ -322,6 +350,11 @@ func (r *Registry) SearchIndex() *SearchIndex {
return r.Get(r.content().SearchIndex.Reference()).(*SearchIndex)
}
// EventManager returns the EventManager singleton
func (r *Registry) EventManager() *EventManager {
return r.Get(r.content().EventManager.Reference()).(*EventManager)
}
// FileManager returns the FileManager singleton
func (r *Registry) FileManager() *FileManager {
return r.Get(r.content().FileManager.Reference()).(*FileManager)
......@@ -336,3 +369,57 @@ func (r *Registry) VirtualDiskManager() *VirtualDiskManager {
func (r *Registry) ViewManager() *ViewManager {
return r.Get(r.content().ViewManager.Reference()).(*ViewManager)
}
// UserDirectory returns the UserDirectory singleton
func (r *Registry) UserDirectory() *UserDirectory {
return r.Get(r.content().UserDirectory.Reference()).(*UserDirectory)
}
// SessionManager returns the SessionManager singleton
func (r *Registry) SessionManager() *SessionManager {
return r.Get(r.content().SessionManager.Reference()).(*SessionManager)
}
func (r *Registry) MarshalJSON() ([]byte, error) {
r.m.Lock()
defer r.m.Unlock()
vars := struct {
Objects int
Locks int
}{
len(r.objects),
len(r.locks),
}
return json.Marshal(vars)
}
func (r *Registry) locker(obj mo.Reference) sync.Locker {
if mu, ok := obj.(sync.Locker); ok {
return mu
}
ref := obj.Reference()
r.m.Lock()
mu, ok := r.locks[ref]
if !ok {
mu = new(sync.Mutex)
r.locks[ref] = mu
}
r.m.Unlock()
return mu
}
var enableLocker = os.Getenv("VCSIM_LOCKER") != "false"
// WithLock holds a lock for the given object while then given function is run.
func (r *Registry) WithLock(obj mo.Reference, f func()) {
if enableLocker {
mu := r.locker(obj)
mu.Lock()
defer mu.Unlock()
}
f()
}
......@@ -252,12 +252,13 @@ func (p *ResourcePool) CreateVApp(req *types.CreateVApp) soap.HasFault {
return body
}
func (a *VirtualApp) CreateChildVMTask(req *types.CreateChildVM_Task) soap.HasFault {
func (a *VirtualApp) CreateChildVMTask(ctx *Context, req *types.CreateChildVM_Task) soap.HasFault {
ctx.Caller = &a.Self
body := &methods.CreateChildVM_TaskBody{}
folder := Map.Get(*a.ParentFolder).(*Folder)
res := folder.CreateVMTask(&types.CreateVM_Task{
res := folder.CreateVMTask(ctx, &types.CreateVM_Task{
This: folder.Self,
Config: req.Config,
Host: req.Host,
......@@ -286,18 +287,19 @@ func (p *ResourcePool) DestroyTask(req *types.Destroy_Task) soap.HasFault {
parent := &pp.ResourcePool
// Remove child reference from rp
parent.ResourcePool = RemoveReference(req.This, parent.ResourcePool)
Map.RemoveReference(parent, &parent.ResourcePool, req.This)
// The grandchildren become children of the parent (rp)
parent.ResourcePool = append(parent.ResourcePool, p.ResourcePool.ResourcePool...)
Map.AppendReference(parent, &parent.ResourcePool, p.ResourcePool.ResourcePool...)
// And VMs move to the parent
vms := p.ResourcePool.Vm
for _, vm := range vms {
Map.Get(vm).(*VirtualMachine).ResourcePool = &parent.Self
for _, ref := range vms {
vm := Map.Get(ref).(*VirtualMachine)
Map.WithLock(vm, func() { vm.ResourcePool = &parent.Self })
}
parent.Vm = append(parent.Vm, vms...)
Map.AppendReference(parent, &parent.Vm, vms...)
Map.Remove(req.This)
......
......@@ -62,6 +62,7 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service
NewLicenseManager(*s.Content.LicenseManager),
NewSearchIndex(*s.Content.SearchIndex),
NewViewManager(*s.Content.ViewManager),
NewEventManager(*s.Content.EventManager),
NewTaskManager(*s.Content.TaskManager),
NewUserDirectory(*s.Content.UserDirectory),
NewOptionManager(s.Content.Setting, setting),
......@@ -75,6 +76,10 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service
objects = append(objects, NewIpPoolManager(*s.Content.IpPoolManager))
}
if s.Content.AccountManager != nil {
objects = append(objects, NewHostLocalAccountManager(*s.Content.AccountManager))
}
for _, o := range objects {
Map.Put(o)
}
......
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -17,6 +17,10 @@ limitations under the License.
package simulator
import (
"context"
"fmt"
"net/http"
"strings"
"time"
"github.com/google/uuid"
......@@ -33,26 +37,30 @@ type SessionManager struct {
mo.SessionManager
ServiceHostName string
sessions map[string]Session
}
func NewSessionManager(ref types.ManagedObjectReference) object.Reference {
s := &SessionManager{}
s := &SessionManager{
sessions: make(map[string]Session),
}
s.Self = ref
return s
}
func (s *SessionManager) Login(login *types.Login) soap.HasFault {
func (s *SessionManager) Login(ctx *Context, login *types.Login) soap.HasFault {
body := &methods.LoginBody{}
if login.Locale == "" {
login.Locale = session.Locale
}
if login.UserName == "" || login.Password == "" {
body.Fault_ = Fault("Login failure", &types.InvalidLogin{})
if login.UserName == "" || login.Password == "" || ctx.Session != nil {
body.Fault_ = invalidLogin
} else {
body.Res = &types.LoginResponse{
Returnval: types.UserSession{
session := Session{
UserSession: types.UserSession{
Key: uuid.New().String(),
UserName: login.UserName,
FullName: login.UserName,
......@@ -61,16 +69,80 @@ func (s *SessionManager) Login(login *types.Login) soap.HasFault {
Locale: login.Locale,
MessageLocale: login.Locale,
},
Registry: NewRegistry(),
}
ctx.SetSession(session, true)
body.Res = &types.LoginResponse{
Returnval: session.UserSession,
}
}
return body
}
func (s *SessionManager) Logout(*types.Logout) soap.HasFault {
func (s *SessionManager) Logout(ctx *Context, _ *types.Logout) soap.HasFault {
session := ctx.Session
delete(s.sessions, session.Key)
ctx.postEvent(&types.UserLogoutSessionEvent{
IpAddress: session.IpAddress,
UserAgent: session.UserAgent,
SessionId: session.Key,
LoginTime: &session.LoginTime,
})
return &methods.LogoutBody{Res: new(types.LogoutResponse)}
}
func (s *SessionManager) TerminateSession(ctx *Context, req *types.TerminateSession) soap.HasFault {
body := new(methods.TerminateSessionBody)
for _, id := range req.SessionId {
if id == ctx.Session.Key {
body.Fault_ = Fault("", new(types.InvalidArgument))
return body
}
delete(s.sessions, id)
}
body.Res = new(types.TerminateSessionResponse)
return body
}
func (s *SessionManager) AcquireCloneTicket(ctx *Context, _ *types.AcquireCloneTicket) soap.HasFault {
session := *ctx.Session
session.Key = uuid.New().String()
s.sessions[session.Key] = session
return &methods.AcquireCloneTicketBody{
Res: &types.AcquireCloneTicketResponse{
Returnval: session.Key,
},
}
}
func (s *SessionManager) CloneSession(ctx *Context, ticket *types.CloneSession) soap.HasFault {
body := new(methods.CloneSessionBody)
session, exists := s.sessions[ticket.CloneTicket]
if exists {
delete(s.sessions, ticket.CloneTicket) // A clone ticket can only be used once
session.Key = uuid.New().String()
ctx.SetSession(session, true)
body.Res = &types.CloneSessionResponse{
Returnval: session.UserSession,
}
} else {
body.Fault_ = invalidLogin
}
return body
}
func (s *SessionManager) AcquireGenericServiceTicket(ticket *types.AcquireGenericServiceTicket) soap.HasFault {
return &methods.AcquireGenericServiceTicketBody{
Res: &types.AcquireGenericServiceTicketResponse{
......@@ -81,3 +153,125 @@ func (s *SessionManager) AcquireGenericServiceTicket(ticket *types.AcquireGeneri
},
}
}
// internalContext is the session for use by the in-memory client (Service.RoundTrip)
var internalContext = &Context{
Context: context.Background(),
Session: &Session{
UserSession: types.UserSession{
Key: uuid.New().String(),
},
Registry: NewRegistry(),
},
}
var invalidLogin = Fault("Login failure", new(types.InvalidLogin))
// Context provides per-request Session management.
type Context struct {
req *http.Request
res http.ResponseWriter
m *SessionManager
context.Context
Session *Session
Caller *types.ManagedObjectReference
}
// mapSession maps an HTTP cookie to a Session.
func (c *Context) mapSession() {
if cookie, err := c.req.Cookie(soap.SessionCookieName); err == nil {
if val, ok := c.m.sessions[cookie.Value]; ok {
c.SetSession(val, false)
}
}
}
// SetSession should be called after successful authentication.
func (c *Context) SetSession(session Session, login bool) {
session.UserAgent = c.req.UserAgent()
session.IpAddress = strings.Split(c.req.RemoteAddr, ":")[0]
session.LastActiveTime = time.Now()
c.m.sessions[session.Key] = session
c.Session = &session
if login {
http.SetCookie(c.res, &http.Cookie{
Name: soap.SessionCookieName,
Value: session.Key,
})
c.postEvent(&types.UserLoginSessionEvent{
SessionId: session.Key,
IpAddress: session.IpAddress,
UserAgent: session.UserAgent,
Locale: session.Locale,
})
}
}
// WithLock holds a lock for the given object while then given function is run.
func (c *Context) WithLock(obj mo.Reference, f func()) {
if c.Caller != nil && *c.Caller == obj.Reference() {
// Internal method invocation, obj is already locked
f()
return
}
Map.WithLock(obj, f)
}
// postEvent wraps EventManager.PostEvent for internal use, with a lock on the EventManager.
func (c *Context) postEvent(events ...types.BaseEvent) {
m := Map.EventManager()
c.WithLock(m, func() {
for _, event := range events {
m.PostEvent(c, &types.PostEvent{EventToPost: event})
}
})
}
// Session combines a UserSession and a Registry for per-session managed objects.
type Session struct {
types.UserSession
*Registry
}
// Put wraps Registry.Put, setting the moref value to include the session key.
func (s *Session) Put(item mo.Reference) mo.Reference {
ref := item.Reference()
if ref.Value == "" {
ref.Value = fmt.Sprintf("session[%s]%s", s.Key, uuid.New())
}
s.Registry.setReference(item, ref)
return s.Registry.Put(item)
}
// Get wraps Registry.Get, session-izing singleton objects such as SessionManager and the root PropertyCollector.
func (s *Session) Get(ref types.ManagedObjectReference) mo.Reference {
obj := s.Registry.Get(ref)
if obj != nil {
return obj
}
// Return a session "view" of certain singleton objects
switch ref.Type {
case "SessionManager":
// Clone SessionManager so the PropertyCollector can properly report CurrentSession
m := *Map.SessionManager()
m.CurrentSession = &s.UserSession
// TODO: we could maintain SessionList as part of the SessionManager singleton
for _, session := range m.sessions {
m.SessionList = append(m.SessionList, session.UserSession)
}
return &m
case "PropertyCollector":
if ref == Map.content().PropertyCollector {
return s.Put(NewPropertyCollector(ref))
}
}
return Map.Get(ref)
}
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -59,10 +59,12 @@ type Method struct {
// Service decodes incoming requests and dispatches to a Handler
type Service struct {
client *vim25.Client
sm *SessionManager
readAll func(io.Reader) ([]byte, error)
TLS *tls.Config
TLS *tls.Config
ServeMux *http.ServeMux
}
// Server provides a simulator Service over HTTP
......@@ -77,6 +79,7 @@ type Server struct {
func New(instance *ServiceInstance) *Service {
s := &Service{
readAll: ioutil.ReadAll,
sm: Map.SessionManager(),
}
s.client, _ = vim25.NewClient(context.Background(), s)
......@@ -106,8 +109,29 @@ func Fault(msg string, fault types.BaseMethodFault) *soap.Fault {
return f
}
func (s *Service) call(method *Method) soap.HasFault {
func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
handler := Map.Get(method.This)
session := ctx.Session
if session == nil {
switch method.Name {
case "RetrieveServiceContent", "Login", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
// ok for now, TODO: authz
default:
fault := &types.NotAuthenticated{
NoPermission: types.NoPermission{
Object: method.This,
PrivilegeId: "System.View",
},
}
return &serverFaultBody{Reason: Fault("", fault)}
}
} else {
// Prefer the Session.Registry, ServiceContent.PropertyCollector filter field for example is per-session
if h := session.Get(method.This); h != nil {
handler = h
}
}
if handler == nil {
msg := fmt.Sprintf("managed object not found: %s", method.This)
......@@ -141,7 +165,14 @@ func (s *Service) call(method *Method) soap.HasFault {
}
}
res := m.Call([]reflect.Value{reflect.ValueOf(method.Body)})
var args, res []reflect.Value
if m.Type().NumIn() == 2 {
args = append(args, reflect.ValueOf(ctx))
}
args = append(args, reflect.ValueOf(method.Body))
Map.WithLock(handler, func() {
res = m.Call(args)
})
return res[0].Interface().(soap.HasFault)
}
......@@ -165,7 +196,10 @@ func (s *Service) RoundTrip(ctx context.Context, request, response soap.HasFault
Body: req.Interface(),
}
res := s.call(method)
res := s.call(&Context{
Context: ctx,
Session: internalContext.Session,
}, method)
if err := res.Fault(); err != nil {
return soap.WrapSoapFault(err)
......@@ -226,7 +260,11 @@ func (s *Service) About(w http.ResponseWriter, r *http.Request) {
}
seen[m.Name] = true
if m.Type.NumIn() != 2 || m.Type.NumOut() != 1 || m.Type.Out(0) != f {
in := m.Type.NumIn()
if in < 2 || in > 3 { // at least 2 params (receiver and request), optionally a 3rd param (context)
continue
}
if m.Type.NumOut() != 1 || m.Type.Out(0) != f { // all methods return soap.HasFault
continue
}
......@@ -262,6 +300,15 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(os.Stderr, "Request: %s\n", string(body))
}
ctx := &Context{
req: r,
res: w,
m: s.sm,
Context: context.Background(),
}
Map.WithLock(s.sm, ctx.mapSession)
var res soap.HasFault
var soapBody interface{}
......@@ -269,7 +316,7 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
if err != nil {
res = serverFault(err.Error())
} else {
res = s.call(method)
res = s.call(ctx, method)
}
if f := res.Fault(); f != nil {
......@@ -349,20 +396,10 @@ func (s *Service) ServeDatastore(w http.ResponseWriter, r *http.Request) {
return
}
file := strings.TrimPrefix(r.URL.Path, folderPrefix)
p := path.Join(ds.Info.GetDatastoreInfo().Url, file)
r.URL.Path = strings.TrimPrefix(r.URL.Path, folderPrefix)
p := path.Join(ds.Info.GetDatastoreInfo().Url, r.URL.Path)
switch r.Method {
case "GET":
f, err := os.Open(p)
if err != nil {
log.Printf("failed to %s '%s': %s", r.Method, p, err)
w.WriteHeader(http.StatusNotFound)
return
}
defer f.Close()
_, _ = io.Copy(w, f)
case "POST":
_, err := os.Stat(p)
if err == nil {
......@@ -384,7 +421,9 @@ func (s *Service) ServeDatastore(w http.ResponseWriter, r *http.Request) {
_, _ = io.Copy(f, r.Body)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
fs := http.FileServer(http.Dir(ds.Info.GetDatastoreInfo().Url))
fs.ServeHTTP(w, r)
}
}
......@@ -408,7 +447,11 @@ func (*Service) ServiceVersions(w http.ResponseWriter, r *http.Request) {
// NewServer returns an http Server instance for the given service
func (s *Service) NewServer() *Server {
mux := http.NewServeMux()
mux := s.ServeMux
if mux == nil {
mux = http.NewServeMux()
}
path := "/sdk"
mux.HandleFunc(path, s.ServeSDK)
......@@ -428,7 +471,7 @@ func (s *Service) NewServer() *Server {
}
// Redirect clients to this http server, rather than HostSystem.Name
Map.Get(*s.client.ServiceContent.SessionManager).(*SessionManager).ServiceHostName = u.Host
Map.SessionManager().ServiceHostName = u.Host
if f := flag.Lookup("httptest.serve"); f != nil {
// Avoid the blocking behaviour of httptest.Server.Start() when this flag is set
......@@ -491,7 +534,23 @@ func (s *Server) Close() {
}
}
var typeFunc = types.TypeFunc()
var (
vim25MapType = types.TypeFunc()
typeFunc = defaultMapType
)
func defaultMapType(name string) (reflect.Type, bool) {
typ, ok := vim25MapType(name)
if !ok {
// See TestIssue945, in which case Go does not resolve the namespace and name == "ns1:TraversalSpec"
// Without this hack, the SelectSet would be all nil's
kind := strings.SplitN(name, ":", 2)
if len(kind) == 2 {
typ, ok = vim25MapType(kind[1])
}
}
return typ, ok
}
// UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type
func UnmarshalBody(data []byte) (*Method, error) {
......
......@@ -32,13 +32,14 @@ func (v *VirtualMachineSnapshot) RemoveSnapshotTask(req *types.RemoveSnapshot_Ta
Map.Remove(req.This)
vm := Map.Get(v.Vm).(*VirtualMachine)
Map.WithLock(vm, func() {
if vm.Snapshot.CurrentSnapshot != nil && *vm.Snapshot.CurrentSnapshot == req.This {
parent := findParentSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This)
vm.Snapshot.CurrentSnapshot = parent
}
if vm.Snapshot.CurrentSnapshot != nil && *vm.Snapshot.CurrentSnapshot == req.This {
parent := findParentSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This)
vm.Snapshot.CurrentSnapshot = parent
}
vm.Snapshot.RootSnapshotList = removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren)
vm.Snapshot.RootSnapshotList = removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren)
})
return nil, nil
})
......@@ -54,8 +55,7 @@ func (v *VirtualMachineSnapshot) RevertToSnapshotTask(req *types.RevertToSnapsho
task := CreateTask(v, "revertToSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
vm := Map.Get(v.Vm).(*VirtualMachine)
ref := v.Reference()
vm.Snapshot.CurrentSnapshot = &ref
Map.WithLock(vm, func() { vm.Snapshot.CurrentSnapshot = &v.Self })
return nil, nil
})
......
......@@ -50,22 +50,54 @@ func NewUserDirectory(ref types.ManagedObjectReference) object.Reference {
func (u *UserDirectory) RetrieveUserGroups(req *types.RetrieveUserGroups) soap.HasFault {
compare := compareFunc(req.SearchStr, req.ExactMatch)
var res []types.BaseUserSearchResult
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 {
if req.FindUsers && !ug.Group || req.FindGroups && ug.Group {
if findUsers && !ug.Group || findGroups && ug.Group {
if compare(ug.Principal) {
res = append(res, ug)
}
}
}
body := &methods.RetrieveUserGroupsBody{
Res: &types.RetrieveUserGroupsResponse{
Returnval: res,
},
return res
}
func (u *UserDirectory) addUser(id string) {
u.add(id, false)
}
func (u *UserDirectory) removeUser(id string) {
u.remove(id, false)
}
func (u *UserDirectory) add(id string, group bool) {
user := &types.UserSearchResult{
FullName: id,
Group: group,
Principal: id,
}
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 {
......
......@@ -71,14 +71,14 @@ func NewViewManager(ref types.ManagedObjectReference) object.Reference {
func destroyView(ref types.ManagedObjectReference) soap.HasFault {
m := Map.ViewManager()
m.ViewList = RemoveReference(ref, m.ViewList)
RemoveReference(&m.ViewList, ref)
return &methods.DestroyViewBody{
Res: &types.DestroyViewResponse{},
}
}
func (m *ViewManager) CreateContainerView(req *types.CreateContainerView) soap.HasFault {
func (m *ViewManager) CreateContainerView(ctx *Context, req *types.CreateContainerView) soap.HasFault {
body := &methods.CreateContainerViewBody{}
root := Map.Get(req.Container)
......@@ -117,7 +117,7 @@ func (m *ViewManager) CreateContainerView(req *types.CreateContainerView) soap.H
}
}
Map.Put(container)
ctx.Session.Put(container)
m.ViewList = append(m.ViewList, container.Reference())
......@@ -125,7 +125,8 @@ func (m *ViewManager) CreateContainerView(req *types.CreateContainerView) soap.H
Returnval: container.Self,
}
container.add(root)
seen := make(map[types.ManagedObjectReference]bool)
container.add(root, seen)
return body
}
......@@ -148,7 +149,7 @@ func (v *ContainerView) include(o types.ManagedObjectReference) bool {
return v.types[o.Type]
}
func (v *ContainerView) add(root mo.Reference) {
func walk(root mo.Reference, f func(child types.ManagedObjectReference)) {
var children []types.ManagedObjectReference
switch e := root.(type) {
......@@ -173,12 +174,21 @@ func (v *ContainerView) add(root mo.Reference) {
}
for _, child := range children {
f(child)
}
}
func (v *ContainerView) add(root mo.Reference, seen map[types.ManagedObjectReference]bool) {
walk(root, func(child types.ManagedObjectReference) {
if v.include(child) {
v.View = AddReference(child, v.View)
if seen[child] == false {
seen[child] = true
v.View = append(v.View, child)
}
}
if v.Recursive {
v.add(Map.Get(child))
v.add(Map.Get(child), seen)
}
}
})
}
......@@ -45,7 +45,7 @@ func (m *VirtualDiskManager) names(name string) []string {
}
}
func (m *VirtualDiskManager) createVirtualDisk(req *types.CreateVirtualDisk_Task) types.BaseMethodFault {
func (m *VirtualDiskManager) createVirtualDisk(op types.VirtualDeviceConfigSpecFileOperation, req *types.CreateVirtualDisk_Task) types.BaseMethodFault {
fm := Map.FileManager()
file, fault := fm.resolve(req.Datacenter, req.Name)
......@@ -53,10 +53,23 @@ func (m *VirtualDiskManager) createVirtualDisk(req *types.CreateVirtualDisk_Task
return fault
}
shouldReplace := op == types.VirtualDeviceConfigSpecFileOperationReplace
shouldExist := op == ""
for _, name := range m.names(file) {
_, err := os.Stat(name)
if err == nil {
if shouldExist {
return nil
}
if shouldReplace {
if err = os.Truncate(file, 0); err != nil {
return fm.fault(name, err, new(types.CannotCreateFile))
}
return nil
}
return fm.fault(name, nil, new(types.FileAlreadyExists))
} else if shouldExist {
return fm.fault(name, nil, new(types.FileNotFound))
}
f, err := os.Create(name)
......@@ -72,7 +85,7 @@ func (m *VirtualDiskManager) createVirtualDisk(req *types.CreateVirtualDisk_Task
func (m *VirtualDiskManager) CreateVirtualDiskTask(req *types.CreateVirtualDisk_Task) soap.HasFault {
task := CreateTask(m, "createVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
return nil, m.createVirtualDisk(req)
return nil, m.createVirtualDisk(types.VirtualDeviceConfigSpecFileOperationCreate, req)
})
return &methods.CreateVirtualDisk_TaskBody{
......
......@@ -57,4 +57,16 @@ var Setting = []types.BaseOptionValue{
Key: "config.vpxd.sso.admin.uri",
Value: "https://127.0.0.1/sso-adminserver/sdk/vsphere.local",
},
&types.OptionValue{
Key: "event.batchsize",
Value: int32(2000),
},
&types.OptionValue{
Key: "event.maxAge",
Value: int32(30),
},
&types.OptionValue{
Key: "event.maxAgeEnabled",
Value: bool(true),
},
}
......@@ -18,6 +18,7 @@ package progress
import (
"container/list"
"context"
"fmt"
"io"
"sync/atomic"
......@@ -75,16 +76,16 @@ type reader struct {
pos int64
size int64
bps uint64
bps uint64
ch chan<- Report
ch chan<- Report
ctx context.Context
}
func NewReader(s Sinker, r io.Reader, size int64) *reader {
func NewReader(ctx context.Context, s Sinker, r io.Reader, size int64) *reader {
pr := reader{
r: r,
r: r,
ctx: ctx,
size: size,
}
......@@ -111,7 +112,10 @@ func (r *reader) Read(b []byte) (int, error) {
bps: &r.bps,
}
r.ch <- q
select {
case r.ch <- q:
case <-r.ctx.Done():
}
return n, err
}
......@@ -127,8 +131,11 @@ func (r *reader) Done(err error) {
err: err,
}
r.ch <- q
close(r.ch)
select {
case r.ch <- q:
close(r.ch)
case <-r.ctx.Done():
}
}
// newBpsLoop returns a sink that monitors and stores throughput.
......
/*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -56,6 +56,7 @@ const (
DefaultVimNamespace = "urn:vim25"
DefaultVimVersion = "6.5"
DefaultMinVimVersion = "5.5"
SessionCookieName = "vmware_soap_session"
)
type header struct {
......@@ -168,7 +169,7 @@ func (c *Client) NewServiceClient(path string, namespace string) *Client {
// Set SOAP Header cookie
for _, cookie := range client.Jar.Cookies(u) {
if cookie.Name == "vmware_soap_session" {
if cookie.Name == SessionCookieName {
client.cookie = cookie.Value
break
}
......@@ -458,6 +459,8 @@ func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error
panic(err)
}
req = req.WithContext(ctx)
req.Header.Set(`Content-Type`, `text/xml; charset="utf-8"`)
soapAction := fmt.Sprintf("%s/%s", c.Namespace, c.Version)
req.Header.Set(`SOAPAction`, soapAction)
......@@ -548,11 +551,11 @@ var DefaultUpload = Upload{
}
// Upload PUTs the local file to the given URL
func (c *Client) Upload(f io.Reader, u *url.URL, param *Upload) error {
func (c *Client) Upload(ctx context.Context, f io.Reader, u *url.URL, param *Upload) error {
var err error
if param.Progress != nil {
pr := progress.NewReader(param.Progress, f, param.ContentLength)
pr := progress.NewReader(ctx, param.Progress, f, param.ContentLength)
f = pr
// Mark progress reader as done when returning from this function.
......@@ -566,6 +569,8 @@ func (c *Client) Upload(f io.Reader, u *url.URL, param *Upload) error {
return err
}
req = req.WithContext(ctx)
req.ContentLength = param.ContentLength
req.Header.Set("Content-Type", param.Type)
......@@ -593,7 +598,7 @@ func (c *Client) Upload(f io.Reader, u *url.URL, param *Upload) error {
}
// UploadFile PUTs the local file to the given URL
func (c *Client) UploadFile(file string, u *url.URL, param *Upload) error {
func (c *Client) UploadFile(ctx context.Context, file string, u *url.URL, param *Upload) error {
if param == nil {
p := DefaultUpload // Copy since we set ContentLength
param = &p
......@@ -612,7 +617,7 @@ func (c *Client) UploadFile(file string, u *url.URL, param *Upload) error {
param.ContentLength = s.Size()
return c.Upload(f, u, param)
return c.Upload(ctx, f, u, param)
}
type Download struct {
......@@ -628,12 +633,14 @@ var DefaultDownload = Download{
}
// DownloadRequest wraps http.Client.Do, returning the http.Response without checking its StatusCode
func (c *Client) DownloadRequest(u *url.URL, param *Download) (*http.Response, error) {
func (c *Client) DownloadRequest(ctx context.Context, u *url.URL, param *Download) (*http.Response, error) {
req, err := http.NewRequest(param.Method, u.String(), nil)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
for k, v := range param.Headers {
req.Header.Add(k, v)
}
......@@ -646,8 +653,8 @@ func (c *Client) DownloadRequest(u *url.URL, param *Download) (*http.Response, e
}
// Download GETs the remote file from the given URL
func (c *Client) Download(u *url.URL, param *Download) (io.ReadCloser, int64, error) {
res, err := c.DownloadRequest(u, param)
func (c *Client) Download(ctx context.Context, u *url.URL, param *Download) (io.ReadCloser, int64, error) {
res, err := c.DownloadRequest(ctx, u, param)
if err != nil {
return nil, 0, err
}
......@@ -667,7 +674,7 @@ func (c *Client) Download(u *url.URL, param *Download) (io.ReadCloser, int64, er
return r, res.ContentLength, nil
}
func (c *Client) WriteFile(file string, src io.Reader, size int64, s progress.Sinker, w io.Writer) error {
func (c *Client) WriteFile(ctx context.Context, file string, src io.Reader, size int64, s progress.Sinker, w io.Writer) error {
var err error
r := src
......@@ -678,7 +685,7 @@ func (c *Client) WriteFile(file string, src io.Reader, size int64, s progress.Si
}
if s != nil {
pr := progress.NewReader(s, src, size)
pr := progress.NewReader(ctx, s, src, size)
src = pr
// Mark progress reader as done when returning from this function.
......@@ -705,16 +712,16 @@ func (c *Client) WriteFile(file string, src io.Reader, size int64, s progress.Si
}
// DownloadFile GETs the given URL to a local file
func (c *Client) DownloadFile(file string, u *url.URL, param *Download) error {
func (c *Client) DownloadFile(ctx context.Context, file string, u *url.URL, param *Download) error {
var err error
if param == nil {
param = &DefaultDownload
}
rc, contentLength, err := c.Download(u, param)
rc, contentLength, err := c.Download(ctx, u, param)
if err != nil {
return err
}
return c.WriteFile(file, rc, contentLength, param.Progress, param.Writer)
return c.WriteFile(ctx, file, rc, contentLength, param.Progress, param.Writer)
}
......@@ -17,6 +17,7 @@ limitations under the License.
package types
import (
"reflect"
"strings"
"time"
)
......@@ -86,3 +87,9 @@ func DefaultResourceConfigSpec() ResourceConfigSpec {
MemoryAllocation: defaultResourceAllocationInfo(),
}
}
func init() {
// Known 6.5 issue where this event type is sent even though it is internal.
// This workaround allows us to unmarshal and avoid NPEs.
t["HostSubSpecificationUpdateEvent"] = reflect.TypeOf((*HostEvent)(nil)).Elem()
}
......@@ -48188,7 +48188,7 @@ func init() {
type VirtualMachineAffinityInfo struct {
DynamicData
AffinitySet []int32 `xml:"affinitySet,omitempty"`
AffinitySet []int32 `xml:"affinitySet"`
}
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