github.com/vmware/govmomi@v0.51.0/vslm/simulator/simulator.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package simulator
     6  
     7  import (
     8  	"cmp"
     9  	"fmt"
    10  	"slices"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/vmware/govmomi/simulator"
    16  	"github.com/vmware/govmomi/vim25"
    17  	vimx "github.com/vmware/govmomi/vim25/methods"
    18  	"github.com/vmware/govmomi/vim25/soap"
    19  	vim "github.com/vmware/govmomi/vim25/types"
    20  	"github.com/vmware/govmomi/vslm"
    21  	"github.com/vmware/govmomi/vslm/methods"
    22  	"github.com/vmware/govmomi/vslm/types"
    23  )
    24  
    25  var content = types.VslmServiceInstanceContent{
    26  	AboutInfo: types.VslmAboutInfo{
    27  		Name:         "VMware Virtual Storage Lifecycle Manager Service",
    28  		FullName:     "VMware Virtual Storage Lifecycle Manager Service 1.0.0",
    29  		Vendor:       "VMware, Inc.",
    30  		ApiVersion:   "1.0.0",
    31  		InstanceUuid: "31c68687-4f1e-4247-8158-f31d1ce95bbe",
    32  	},
    33  	SessionManager:          vim.ManagedObjectReference{Type: "VslmSessionManager", Value: "SessionManager"},
    34  	VStorageObjectManager:   vim.ManagedObjectReference{Type: "VslmVStorageObjectManager", Value: "VStorageObjectManager"},
    35  	StorageLifecycleManager: vim.ManagedObjectReference{Type: "VslmStorageLifecycleManager", Value: "StorageLifecycleManager"},
    36  }
    37  
    38  func init() {
    39  	simulator.RegisterEndpoint(func(s *simulator.Service, r *simulator.Registry) {
    40  		if r.IsVPX() {
    41  			s.RegisterSDK(New())
    42  		}
    43  	})
    44  }
    45  
    46  func New() *simulator.Registry {
    47  	r := simulator.NewRegistry()
    48  	r.Namespace = vslm.Namespace
    49  	r.Path = vslm.Path
    50  	r.Cookie = simulator.SOAPCookie
    51  
    52  	r.Put(&ServiceInstance{
    53  		ManagedObjectReference: vslm.ServiceInstance,
    54  		Content:                content,
    55  	})
    56  
    57  	r.Put(&VStorageObjectManager{
    58  		ManagedObjectReference: content.VStorageObjectManager,
    59  	})
    60  
    61  	return r
    62  }
    63  
    64  type ServiceInstance struct {
    65  	vim.ManagedObjectReference
    66  
    67  	Content types.VslmServiceInstanceContent
    68  }
    69  
    70  func (s *ServiceInstance) RetrieveContent(_ *types.RetrieveContent) soap.HasFault {
    71  	return &methods.RetrieveContentBody{
    72  		Res: &types.RetrieveContentResponse{
    73  			Returnval: s.Content,
    74  		},
    75  	}
    76  }
    77  
    78  // VStorageObjectManager APIs manage First Class Disks (FCDs) using the "Global Catalog".
    79  // The majority of methods in this API simulator dispatch to VcenterVStorageObjectManager methods,
    80  // after looking up a disk's Datastore. Along with 'VslmTask', who's methods proxy to a vim25 'Task'.
    81  type VStorageObjectManager struct {
    82  	vim.ManagedObjectReference
    83  }
    84  
    85  func matchesTime(q types.VslmVsoVStorageObjectQuerySpec, val time.Time) (bool, error) {
    86  	src, err := time.Parse(time.RFC3339Nano, q.QueryValue[0])
    87  	if err != nil {
    88  		return false, err
    89  	}
    90  
    91  	switch types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnum(q.QueryOperator) {
    92  	case types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumEquals:
    93  		return src == val, nil
    94  	case types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumLessThan:
    95  		return val.Before(src), nil
    96  	case types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumGreaterThan:
    97  		return val.After(src), nil
    98  	default:
    99  		return false, fmt.Errorf("invalid queryOperator %s for time", q.QueryOperator)
   100  	}
   101  }
   102  
   103  var longOps = map[types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnum]func(int64, int64) bool{
   104  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumEquals:             func(a, b int64) bool { return a == b },
   105  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumNotEquals:          func(a, b int64) bool { return a != b },
   106  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumLessThan:           func(a, b int64) bool { return a < b },
   107  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumGreaterThan:        func(a, b int64) bool { return a > b },
   108  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumLessThanOrEqual:    func(a, b int64) bool { return a < b || a == b },
   109  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumGreaterThanOrEqual: func(a, b int64) bool { return a > b || a == b },
   110  }
   111  
   112  func matchesLong(q types.VslmVsoVStorageObjectQuerySpec, field int64) (bool, error) {
   113  	num, err := strconv.ParseInt(q.QueryValue[0], 10, 64)
   114  	if err != nil {
   115  		return false, err
   116  	}
   117  
   118  	op, ok := longOps[types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnum(q.QueryOperator)]
   119  	if !ok {
   120  		return false, fmt.Errorf("invalid QueryOperator: %s", q.QueryOperator)
   121  	}
   122  
   123  	return op(field, int64(num)), nil
   124  }
   125  
   126  var stringOps = map[types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnum]func(string, string) bool{
   127  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumEquals:             func(a, b string) bool { return a == b },
   128  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumNotEquals:          func(a, b string) bool { return a != b },
   129  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumLessThan:           func(a, b string) bool { return a < b },
   130  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumGreaterThan:        func(a, b string) bool { return a > b },
   131  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumLessThanOrEqual:    func(a, b string) bool { return a < b || a == b },
   132  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumGreaterThanOrEqual: func(a, b string) bool { return a > b || a == b },
   133  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumContains:           strings.Contains,
   134  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumStartsWith:         strings.HasPrefix,
   135  	types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnumEndsWith:           strings.HasSuffix,
   136  }
   137  
   138  func matches(obj *simulator.VStorageObject, q types.VslmVsoVStorageObjectQuerySpec) (bool, error) {
   139  	var field []string
   140  
   141  	switch types.VslmVsoVStorageObjectQuerySpecQueryFieldEnum(q.QueryField) {
   142  	case types.VslmVsoVStorageObjectQuerySpecQueryFieldEnumCapacity:
   143  		return matchesLong(q, obj.Config.CapacityInMB)
   144  	case types.VslmVsoVStorageObjectQuerySpecQueryFieldEnumCreateTime:
   145  		return matchesTime(q, obj.Config.CreateTime)
   146  	case types.VslmVsoVStorageObjectQuerySpecQueryFieldEnumBackingObjectId:
   147  		return false, fmt.Errorf("Query field %s is not supported", q.QueryField) // Same as real VC currently
   148  	case types.VslmVsoVStorageObjectQuerySpecQueryFieldEnumDatastoreMoId:
   149  		return true, nil // Already filtered datastores
   150  	case types.VslmVsoVStorageObjectQuerySpecQueryFieldEnumId:
   151  		field = append(field, obj.Config.Id.Id)
   152  	case types.VslmVsoVStorageObjectQuerySpecQueryFieldEnumName:
   153  		field = append(field, obj.Config.Name)
   154  	case types.VslmVsoVStorageObjectQuerySpecQueryFieldEnumMetadataKey:
   155  		field = make([]string, len(obj.Metadata))
   156  		for i := range obj.Metadata {
   157  			field[i] = obj.Metadata[i].Key
   158  		}
   159  	case types.VslmVsoVStorageObjectQuerySpecQueryFieldEnumMetadataValue:
   160  		field = make([]string, len(obj.Metadata))
   161  		for i := range obj.Metadata {
   162  			field[i] = obj.Metadata[i].Value
   163  		}
   164  	default:
   165  		return false, fmt.Errorf("invalid QueryField: %s", q.QueryField)
   166  	}
   167  
   168  	op, ok := stringOps[types.VslmVsoVStorageObjectQuerySpecQueryOperatorEnum(q.QueryOperator)]
   169  	if !ok {
   170  		return false, fmt.Errorf("invalid QueryOperator: %s", q.QueryOperator)
   171  	}
   172  
   173  	for _, f := range field {
   174  		if op(f, q.QueryValue[0]) {
   175  			return true, nil
   176  		}
   177  	}
   178  
   179  	return false, nil
   180  }
   181  
   182  var (
   183  	invalidValues = &vim.InvalidArgument{InvalidProperty: "values"}
   184  
   185  	invalidQuery = &vim.SystemError{
   186  		RuntimeFault: vim.RuntimeFault{
   187  			MethodFault: vim.MethodFault{
   188  				FaultCause: &vim.LocalizedMethodFault{
   189  					Fault: &types.VslmFault{
   190  						Msg: "Unexpected exception",
   191  					},
   192  				},
   193  			},
   194  		},
   195  		Reason: "Undeclared fault",
   196  	}
   197  )
   198  
   199  func matchesSpec(obj *simulator.VStorageObject, query []types.VslmVsoVStorageObjectQuerySpec) (bool, *soap.Fault) {
   200  	for _, q := range query {
   201  		if len(q.QueryValue) != 1 { // Only 1 value is currently supported by vCenter
   202  			return false, simulator.Fault("", invalidValues)
   203  		}
   204  		match, err := matches(obj, q)
   205  		if err != nil {
   206  			return false, simulator.Fault(err.Error(), invalidQuery)
   207  		}
   208  		if !match {
   209  			return false, nil
   210  		}
   211  	}
   212  	return true, nil
   213  }
   214  
   215  func (m *VStorageObjectManager) VslmListVStorageObjectForSpec(ctx *simulator.Context, req *types.VslmListVStorageObjectForSpec) soap.HasFault {
   216  	body := new(methods.VslmListVStorageObjectForSpecBody)
   217  	vctx := ctx.For(vim25.Path)
   218  	vsom := vctx.Map.VStorageObjectManager()
   219  
   220  	datastores := map[vim.ManagedObjectReference]bool{}
   221  	for _, q := range req.Query {
   222  		if q.QueryField == string(types.VslmVsoVStorageObjectQuerySpecQueryFieldEnumDatastoreMoId) {
   223  			for _, id := range q.QueryValue {
   224  				datastores[vim.ManagedObjectReference{
   225  					Type:  "Datastore",
   226  					Value: "datastore-" + id,
   227  				}] = true
   228  			}
   229  		}
   230  	}
   231  
   232  	var catalog []vim.VStorageObject
   233  
   234  	for ds, objs := range vsom.Catalog() {
   235  		if len(datastores) != 0 && !datastores[ds] {
   236  			continue
   237  		}
   238  
   239  		for _, obj := range objs {
   240  			matches, err := matchesSpec(obj, req.Query)
   241  			if err != nil {
   242  				body.Fault_ = err
   243  				return body
   244  			}
   245  			if !matches {
   246  				continue
   247  			}
   248  
   249  			catalog = append(catalog, obj.VStorageObject)
   250  		}
   251  	}
   252  
   253  	// Sort as real VC does, required to support pagination
   254  	slices.SortFunc(catalog, func(a, b vim.VStorageObject) int {
   255  		return cmp.Compare(a.Config.Id.Id, b.Config.Id.Id)
   256  	})
   257  
   258  	res := &types.VslmVsoVStorageObjectQueryResult{
   259  		AllRecordsReturned: true,
   260  	}
   261  
   262  	for _, obj := range catalog {
   263  		res.Id = append(res.Id, obj.Config.Id)
   264  
   265  		vso := types.VslmVsoVStorageObjectResult{
   266  			Id:           obj.Config.Id,
   267  			Name:         obj.Config.Name,
   268  			CapacityInMB: obj.Config.CapacityInMB,
   269  			CreateTime:   &obj.Config.CreateTime,
   270  		}
   271  
   272  		res.QueryResults = append(res.QueryResults, vso)
   273  
   274  		if len(res.QueryResults) >= int(req.MaxResult) {
   275  			res.AllRecordsReturned = false
   276  			break
   277  		}
   278  	}
   279  
   280  	body.Res = &types.VslmListVStorageObjectForSpecResponse{
   281  		Returnval: res,
   282  	}
   283  
   284  	return body
   285  }
   286  
   287  func (m *VStorageObjectManager) VslmRetrieveVStorageObject(ctx *simulator.Context, req *types.VslmRetrieveVStorageObject) soap.HasFault {
   288  	body := new(methods.VslmRetrieveVStorageObjectBody)
   289  
   290  	vctx := ctx.For(vim25.Path)
   291  	vsom := vctx.Map.VStorageObjectManager()
   292  
   293  	for _, objs := range vsom.Catalog() {
   294  		for id, obj := range objs {
   295  			if id == req.Id {
   296  				body.Res = &types.VslmRetrieveVStorageObjectResponse{
   297  					Returnval: obj.VStorageObject,
   298  				}
   299  				return body
   300  			}
   301  		}
   302  	}
   303  
   304  	body.Fault_ = simulator.Fault("", &vim.InvalidArgument{InvalidProperty: "VolumeId"})
   305  
   306  	return body
   307  }
   308  
   309  func (m *VStorageObjectManager) VslmReconcileDatastoreInventoryTask(ctx *simulator.Context, req *types.VslmReconcileDatastoreInventory_Task) soap.HasFault {
   310  	body := new(methods.VslmReconcileDatastoreInventory_TaskBody)
   311  
   312  	vctx := ctx.For(vim25.Path)
   313  	vsom := vctx.Map.VStorageObjectManager()
   314  
   315  	val := vsom.ReconcileDatastoreInventoryTask(vctx, &vim.ReconcileDatastoreInventory_Task{
   316  		This:      vsom.Self,
   317  		Datastore: req.Datastore,
   318  	})
   319  
   320  	if val.Fault() != nil {
   321  		body.Fault_ = val.Fault()
   322  	} else {
   323  		ref := val.(*vimx.ReconcileDatastoreInventory_TaskBody).Res.Returnval
   324  
   325  		body.Res = &types.VslmReconcileDatastoreInventory_TaskResponse{
   326  			Returnval: newVslmTask(ctx, ref),
   327  		}
   328  	}
   329  
   330  	return body
   331  }
   332  
   333  func (m *VStorageObjectManager) VslmRegisterDisk(ctx *simulator.Context, req *types.VslmRegisterDisk) soap.HasFault {
   334  	body := new(methods.VslmRegisterDiskBody)
   335  
   336  	vctx := ctx.For(vim25.Path)
   337  	vsom := vctx.Map.VStorageObjectManager()
   338  
   339  	val := vsom.RegisterDisk(vctx, &vim.RegisterDisk{
   340  		This: vsom.Self,
   341  		Path: req.Path,
   342  		Name: req.Name,
   343  	})
   344  
   345  	if val.Fault() != nil {
   346  		body.Fault_ = val.Fault()
   347  	} else {
   348  		body.Res = &types.VslmRegisterDiskResponse{
   349  			Returnval: val.(*vimx.RegisterDiskBody).Res.Returnval,
   350  		}
   351  	}
   352  
   353  	return body
   354  }
   355  
   356  func (m *VStorageObjectManager) VslmCreateDiskTask(ctx *simulator.Context, req *types.VslmCreateDisk_Task) soap.HasFault {
   357  	body := new(methods.VslmCreateDisk_TaskBody)
   358  
   359  	vctx := ctx.For(vim25.Path)
   360  	vsom := vctx.Map.VStorageObjectManager()
   361  
   362  	val := vsom.CreateDiskTask(vctx, &vim.CreateDisk_Task{
   363  		This: vsom.Self,
   364  		Spec: req.Spec,
   365  	})
   366  
   367  	if val.Fault() != nil {
   368  		body.Fault_ = val.Fault()
   369  	} else {
   370  		ref := val.(*vimx.CreateDisk_TaskBody).Res.Returnval
   371  
   372  		body.Res = &types.VslmCreateDisk_TaskResponse{
   373  			Returnval: newVslmTask(ctx, ref),
   374  		}
   375  	}
   376  
   377  	return body
   378  }
   379  
   380  func (m *VStorageObjectManager) VslmDeleteVStorageObjectTask(ctx *simulator.Context, req *types.VslmDeleteVStorageObject_Task) soap.HasFault {
   381  	body := new(methods.VslmDeleteVStorageObject_TaskBody)
   382  
   383  	vctx := ctx.For(vim25.Path)
   384  	vsom := vctx.Map.VStorageObjectManager()
   385  
   386  	val := vsom.DeleteVStorageObjectTask(vctx, &vim.DeleteVStorageObject_Task{
   387  		This:      vsom.Self,
   388  		Id:        req.Id,
   389  		Datastore: m.ds(vsom, req.Id),
   390  	})
   391  
   392  	if val.Fault() != nil {
   393  		body.Fault_ = val.Fault()
   394  		return body
   395  	} else {
   396  		ref := val.(*vimx.DeleteVStorageObject_TaskBody).Res.Returnval
   397  
   398  		body.Res = &types.VslmDeleteVStorageObject_TaskResponse{
   399  			Returnval: newVslmTask(ctx, ref),
   400  		}
   401  	}
   402  
   403  	return body
   404  }
   405  
   406  func (m *VStorageObjectManager) VslmRetrieveSnapshotInfo(ctx *simulator.Context, req *types.VslmRetrieveSnapshotInfo) soap.HasFault {
   407  	body := new(methods.VslmRetrieveSnapshotInfoBody)
   408  
   409  	vctx := ctx.For(vim25.Path)
   410  	vsom := vctx.Map.VStorageObjectManager()
   411  
   412  	var vso *simulator.VStorageObject
   413  	for _, objs := range vsom.Catalog() {
   414  		for id, obj := range objs {
   415  			if id == req.Id {
   416  				vso = obj
   417  				break
   418  			}
   419  		}
   420  	}
   421  
   422  	if vso == nil {
   423  		body.Fault_ = simulator.Fault("", &vim.InvalidArgument{InvalidProperty: "VolumeId"})
   424  	} else {
   425  		body.Res = &types.VslmRetrieveSnapshotInfoResponse{
   426  			Returnval: vso.VStorageObjectSnapshotInfo,
   427  		}
   428  	}
   429  
   430  	return body
   431  }
   432  
   433  func (m *VStorageObjectManager) VslmCreateSnapshotTask(ctx *simulator.Context, req *types.VslmCreateSnapshot_Task) soap.HasFault {
   434  	body := new(methods.VslmCreateSnapshot_TaskBody)
   435  
   436  	vctx := ctx.For(vim25.Path)
   437  	vsom := vctx.Map.VStorageObjectManager()
   438  
   439  	val := vsom.VStorageObjectCreateSnapshotTask(vctx, &vim.VStorageObjectCreateSnapshot_Task{
   440  		This:        vsom.Self,
   441  		Id:          req.Id,
   442  		Description: req.Description,
   443  		Datastore:   m.ds(vsom, req.Id),
   444  	})
   445  
   446  	if val.Fault() != nil {
   447  		body.Fault_ = val.Fault()
   448  	} else {
   449  		ref := val.(*vimx.VStorageObjectCreateSnapshot_TaskBody).Res.Returnval
   450  
   451  		body.Res = &types.VslmCreateSnapshot_TaskResponse{
   452  			Returnval: newVslmTask(ctx, ref),
   453  		}
   454  	}
   455  
   456  	return body
   457  }
   458  
   459  func (m *VStorageObjectManager) VslmDeleteSnapshotTask(ctx *simulator.Context, req *types.VslmDeleteSnapshot_Task) soap.HasFault {
   460  	body := new(methods.VslmDeleteSnapshot_TaskBody)
   461  
   462  	vctx := ctx.For(vim25.Path)
   463  	vsom := vctx.Map.VStorageObjectManager()
   464  
   465  	val := vsom.DeleteSnapshotTask(vctx, &vim.DeleteSnapshot_Task{
   466  		This:       vsom.Self,
   467  		Id:         req.Id,
   468  		SnapshotId: req.SnapshotId,
   469  		Datastore:  m.ds(vsom, req.Id),
   470  	})
   471  
   472  	if val.Fault() != nil {
   473  		body.Fault_ = val.Fault()
   474  	} else {
   475  		ref := val.(*vimx.DeleteSnapshot_TaskBody).Res.Returnval
   476  
   477  		body.Res = &types.VslmDeleteSnapshot_TaskResponse{
   478  			Returnval: newVslmTask(ctx, ref),
   479  		}
   480  	}
   481  
   482  	return body
   483  }
   484  
   485  func (m *VStorageObjectManager) VslmAttachTagToVStorageObject(ctx *simulator.Context, req *types.VslmAttachTagToVStorageObject) soap.HasFault {
   486  	body := new(methods.VslmAttachTagToVStorageObjectBody)
   487  
   488  	vctx := ctx.For(vim25.Path)
   489  	vsom := vctx.Map.VStorageObjectManager()
   490  
   491  	val := vsom.AttachTagToVStorageObject(vctx, &vim.AttachTagToVStorageObject{
   492  		This:     vsom.Self,
   493  		Id:       req.Id,
   494  		Category: req.Category,
   495  		Tag:      req.Tag,
   496  	})
   497  
   498  	if val.Fault() != nil {
   499  		body.Fault_ = val.Fault()
   500  	} else {
   501  		body.Res = new(types.VslmAttachTagToVStorageObjectResponse)
   502  	}
   503  
   504  	return body
   505  }
   506  
   507  func (m *VStorageObjectManager) VslmDetachTagFromVStorageObject(ctx *simulator.Context, req *types.VslmDetachTagFromVStorageObject) soap.HasFault {
   508  	body := new(methods.VslmDetachTagFromVStorageObjectBody)
   509  
   510  	vctx := ctx.For(vim25.Path)
   511  	vsom := vctx.Map.VStorageObjectManager()
   512  
   513  	val := vsom.DetachTagFromVStorageObject(vctx, &vim.DetachTagFromVStorageObject{
   514  		This:     vsom.Self,
   515  		Id:       req.Id,
   516  		Category: req.Category,
   517  		Tag:      req.Tag,
   518  	})
   519  
   520  	if val.Fault() != nil {
   521  		body.Fault_ = val.Fault()
   522  	} else {
   523  		body.Res = new(types.VslmDetachTagFromVStorageObjectResponse)
   524  	}
   525  
   526  	return body
   527  }
   528  
   529  func (m *VStorageObjectManager) VslmListVStorageObjectsAttachedToTag(ctx *simulator.Context, req *types.VslmListVStorageObjectsAttachedToTag) soap.HasFault {
   530  	body := new(methods.VslmListVStorageObjectsAttachedToTagBody)
   531  
   532  	vctx := ctx.For(vim25.Path)
   533  	vsom := vctx.Map.VStorageObjectManager()
   534  
   535  	val := vsom.ListVStorageObjectsAttachedToTag(vctx, &vim.ListVStorageObjectsAttachedToTag{
   536  		This:     vsom.Self,
   537  		Category: req.Category,
   538  		Tag:      req.Tag,
   539  	})
   540  
   541  	if val.Fault() != nil {
   542  		body.Fault_ = val.Fault()
   543  	} else {
   544  		body.Res = &types.VslmListVStorageObjectsAttachedToTagResponse{
   545  			Returnval: val.(*vimx.ListVStorageObjectBody).Res.Returnval,
   546  		}
   547  	}
   548  
   549  	return body
   550  }
   551  
   552  func (m *VStorageObjectManager) VslmListTagsAttachedToVStorageObject(ctx *simulator.Context, req *types.VslmListTagsAttachedToVStorageObject) soap.HasFault {
   553  	body := new(methods.VslmListTagsAttachedToVStorageObjectBody)
   554  
   555  	vctx := ctx.For(vim25.Path)
   556  	vsom := vctx.Map.VStorageObjectManager()
   557  
   558  	val := vsom.ListTagsAttachedToVStorageObject(vctx, &vim.ListTagsAttachedToVStorageObject{
   559  		This: vsom.Self,
   560  		Id:   req.Id,
   561  	})
   562  
   563  	if val.Fault() != nil {
   564  		body.Fault_ = val.Fault()
   565  	} else {
   566  		body.Res = &types.VslmListTagsAttachedToVStorageObjectResponse{
   567  			Returnval: val.(*vimx.ListTagsAttachedToVStorageObjectBody).Res.Returnval,
   568  		}
   569  	}
   570  
   571  	return body
   572  }
   573  
   574  func (m *VStorageObjectManager) VslmAttachDiskTask(ctx *simulator.Context, req *types.VslmAttachDisk_Task) soap.HasFault {
   575  	body := new(methods.VslmAttachDisk_TaskBody)
   576  
   577  	vctx := ctx.For(vim25.Path)
   578  	vsom := vctx.Map.VStorageObjectManager()
   579  
   580  	vm, ok := vctx.Map.Get(req.Vm).(*simulator.VirtualMachine)
   581  	if !ok {
   582  		body.Fault_ = simulator.Fault("", &vim.ManagedObjectNotFound{Obj: req.Vm})
   583  		return body
   584  	}
   585  
   586  	var val soap.HasFault
   587  
   588  	vctx.WithLock(vm, func() {
   589  		val = vm.AttachDiskTask(vctx, &vim.AttachDisk_Task{
   590  			This:          vm.Self,
   591  			Datastore:     m.ds(vsom, req.Id),
   592  			DiskId:        req.Id,
   593  			ControllerKey: req.ControllerKey,
   594  			UnitNumber:    req.UnitNumber,
   595  		})
   596  	})
   597  
   598  	if val.Fault() != nil {
   599  		body.Fault_ = val.Fault()
   600  	} else {
   601  		ref := val.(*vimx.AttachDisk_TaskBody).Res.Returnval
   602  
   603  		body.Res = &types.VslmAttachDisk_TaskResponse{
   604  			Returnval: newVslmTask(ctx, ref),
   605  		}
   606  	}
   607  
   608  	return body
   609  }
   610  
   611  func (m *VStorageObjectManager) VslmUpdateVStorageObjectMetadataTask(ctx *simulator.Context, req *types.VslmUpdateVStorageObjectMetadata_Task) soap.HasFault {
   612  	body := new(methods.VslmUpdateVStorageObjectMetadata_TaskBody)
   613  
   614  	vctx := ctx.For(vim25.Path)
   615  	vsom := vctx.Map.VStorageObjectManager()
   616  
   617  	val := vsom.VCenterUpdateVStorageObjectMetadataExTask(vctx, &vim.VCenterUpdateVStorageObjectMetadataEx_Task{
   618  		This:       vsom.Self,
   619  		Id:         req.Id,
   620  		Metadata:   req.Metadata,
   621  		DeleteKeys: req.DeleteKeys,
   622  		Datastore:  m.ds(vsom, req.Id),
   623  	})
   624  
   625  	if val.Fault() != nil {
   626  		body.Fault_ = val.Fault()
   627  	} else {
   628  		ref := val.(*vimx.VCenterUpdateVStorageObjectMetadataEx_TaskBody).Res.Returnval
   629  
   630  		body.Res = &types.VslmUpdateVStorageObjectMetadata_TaskResponse{
   631  			Returnval: newVslmTask(ctx, ref),
   632  		}
   633  	}
   634  
   635  	return body
   636  }
   637  
   638  func (m *VStorageObjectManager) VslmRetrieveVStorageObjectMetadata(ctx *simulator.Context, req *types.VslmRetrieveVStorageObjectMetadata) soap.HasFault {
   639  	body := new(methods.VslmRetrieveVStorageObjectMetadataBody)
   640  
   641  	vctx := ctx.For(vim25.Path)
   642  	vsom := vctx.Map.VStorageObjectManager()
   643  
   644  	obj := m.object(vsom, req.Id)
   645  	if obj == nil {
   646  		body.Fault_ = simulator.Fault("", &vim.InvalidArgument{InvalidProperty: "VolumeId"})
   647  	} else {
   648  		body.Res = new(types.VslmRetrieveVStorageObjectMetadataResponse)
   649  
   650  		for _, kv := range obj.Metadata {
   651  			if req.Prefix == "" || strings.HasPrefix(kv.Key, req.Prefix) {
   652  				body.Res.Returnval = append(body.Res.Returnval, kv)
   653  			}
   654  		}
   655  	}
   656  
   657  	return body
   658  }
   659  
   660  func (m *VStorageObjectManager) VslmRetrieveVStorageObjectMetadataValue(ctx *simulator.Context, req *types.VslmRetrieveVStorageObjectMetadataValue) soap.HasFault {
   661  	body := new(methods.VslmRetrieveVStorageObjectMetadataValueBody)
   662  
   663  	vctx := ctx.For(vim25.Path)
   664  	vsom := vctx.Map.VStorageObjectManager()
   665  
   666  	obj := m.object(vsom, req.Id)
   667  	if obj == nil {
   668  		body.Fault_ = simulator.Fault("", &vim.InvalidArgument{InvalidProperty: "VolumeId"})
   669  	} else {
   670  		val, ok := func() (string, bool) {
   671  			for _, data := range obj.Metadata {
   672  				if data.Key == req.Key {
   673  					return data.Value, true
   674  				}
   675  			}
   676  			return "", false
   677  		}()
   678  
   679  		if ok {
   680  			body.Res = &types.VslmRetrieveVStorageObjectMetadataValueResponse{Returnval: val}
   681  		} else {
   682  			body.Fault_ = simulator.Fault("", &vim.KeyNotFound{Key: req.Key})
   683  		}
   684  	}
   685  
   686  	return body
   687  }
   688  
   689  // VslmTask methods are just a proxy to vim25 Task methods
   690  type VslmTask struct {
   691  	vim.ManagedObjectReference
   692  }
   693  
   694  func newVslmTask(ctx *simulator.Context, ref vim.ManagedObjectReference) vim.ManagedObjectReference {
   695  	task := &VslmTask{
   696  		ManagedObjectReference: vim.ManagedObjectReference{
   697  			Type:  "VslmTask",
   698  			Value: ref.Value,
   699  		},
   700  	}
   701  
   702  	return ctx.Map.Put(task).Reference()
   703  }
   704  
   705  func (p *VslmTask) VslmQueryInfo(ctx *simulator.Context, req *types.VslmQueryInfo) soap.HasFault {
   706  	body := new(methods.VslmQueryInfoBody)
   707  
   708  	task, fault := p.task(ctx, req.This)
   709  	if fault != nil {
   710  		body.Fault_ = fault
   711  	} else {
   712  		info := types.VslmTaskInfo{
   713  			Key:           p.Value,
   714  			Task:          p.ManagedObjectReference,
   715  			DescriptionId: "com.vmware.cns.vslm.tasks.createDisk",
   716  			State:         types.VslmTaskInfoState(task.State),
   717  			Error:         task.Error,
   718  			Result:        task.Result,
   719  			QueueTime:     task.QueueTime,
   720  			StartTime:     task.StartTime,
   721  			CompleteTime:  task.CompleteTime,
   722  		}
   723  
   724  		body.Res = &types.VslmQueryInfoResponse{Returnval: info}
   725  	}
   726  
   727  	return body
   728  }
   729  
   730  func (p *VslmTask) VslmQueryTaskResult(ctx *simulator.Context, req *types.VslmQueryTaskResult) soap.HasFault {
   731  	body := new(methods.VslmQueryTaskResultBody)
   732  
   733  	task, fault := p.task(ctx, req.This)
   734  	if fault != nil {
   735  		body.Fault_ = fault
   736  	} else {
   737  		body.Res = &types.VslmQueryTaskResultResponse{Returnval: task.Result}
   738  	}
   739  
   740  	return body
   741  }
   742  
   743  func (*VslmTask) task(ctx *simulator.Context, ref vim.ManagedObjectReference) (vim.TaskInfo, *soap.Fault) {
   744  	ctx = ctx.For(vim25.Path)
   745  
   746  	ref.Type = "Task"
   747  
   748  	if task, ok := ctx.Map.Get(ref).(*simulator.Task); ok {
   749  		unlock := ctx.Map.AcquireLock(ctx, ref)
   750  		defer unlock()
   751  		return task.Info, nil
   752  	}
   753  
   754  	return vim.TaskInfo{}, simulator.Fault("", &vim.ManagedObjectNotFound{Obj: ref})
   755  }
   756  
   757  func (*VStorageObjectManager) ds(vsom *simulator.VcenterVStorageObjectManager, reqID vim.ID) vim.ManagedObjectReference {
   758  	for ds, objs := range vsom.Catalog() {
   759  		for id := range objs {
   760  			if id == reqID {
   761  				return ds
   762  			}
   763  		}
   764  	}
   765  
   766  	// vsom calls will fault as they would when ID is NotFound
   767  	return vim.ManagedObjectReference{Type: "Datastore"}
   768  }
   769  
   770  func (*VStorageObjectManager) object(vsom *simulator.VcenterVStorageObjectManager, reqID vim.ID) *simulator.VStorageObject {
   771  	for _, objs := range vsom.Catalog() {
   772  		for id, obj := range objs {
   773  			if id == reqID {
   774  				return obj
   775  			}
   776  		}
   777  	}
   778  	return nil
   779  }