github.com/vmware/govmomi@v0.51.0/cns/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  	"context"
     9  	"slices"
    10  	"time"
    11  
    12  	"github.com/google/uuid"
    13  
    14  	"github.com/vmware/govmomi/cns"
    15  	"github.com/vmware/govmomi/cns/methods"
    16  	"github.com/vmware/govmomi/cns/types"
    17  	cnstypes "github.com/vmware/govmomi/cns/types"
    18  	pbmtypes "github.com/vmware/govmomi/pbm/types"
    19  	"github.com/vmware/govmomi/simulator"
    20  	"github.com/vmware/govmomi/vim25"
    21  	vim25methods "github.com/vmware/govmomi/vim25/methods"
    22  	"github.com/vmware/govmomi/vim25/soap"
    23  	vim25types "github.com/vmware/govmomi/vim25/types"
    24  )
    25  
    26  func init() {
    27  	simulator.RegisterEndpoint(func(s *simulator.Service, r *simulator.Registry) {
    28  		if r.IsVPX() {
    29  			s.RegisterSDK(New())
    30  		}
    31  	})
    32  }
    33  
    34  func New() *simulator.Registry {
    35  	r := simulator.NewRegistry()
    36  	r.Namespace = cns.Namespace
    37  	r.Path = cns.Path
    38  
    39  	r.Put(&CnsVolumeManager{
    40  		ManagedObjectReference: cns.CnsVolumeManagerInstance,
    41  		volumes:                make(map[vim25types.ManagedObjectReference]map[cnstypes.CnsVolumeId]*cnstypes.CnsVolume),
    42  		attachments:            make(map[cnstypes.CnsVolumeId]vim25types.ManagedObjectReference),
    43  		snapshots:              make(map[cnstypes.CnsVolumeId]map[cnstypes.CnsSnapshotId]*cnstypes.CnsSnapshot),
    44  	})
    45  
    46  	return r
    47  }
    48  
    49  type CnsVolumeManager struct {
    50  	vim25types.ManagedObjectReference
    51  	volumes     map[vim25types.ManagedObjectReference]map[cnstypes.CnsVolumeId]*cnstypes.CnsVolume
    52  	attachments map[cnstypes.CnsVolumeId]vim25types.ManagedObjectReference
    53  	snapshots   map[cnstypes.CnsVolumeId]map[cnstypes.CnsSnapshotId]*cnstypes.CnsSnapshot
    54  }
    55  
    56  const simulatorDiskUUID = "6000c298595bf4575739e9105b2c0c2d"
    57  
    58  func (m *CnsVolumeManager) findDisk(vctx *simulator.Context, createSpec cnstypes.CnsVolumeCreateSpec) (*vim25types.VStorageObject, vim25types.BaseMethodFault) {
    59  	vsom := vctx.Map.VStorageObjectManager()
    60  	details := createSpec.BackingObjectDetails.(*cnstypes.CnsBlockBackingDetails)
    61  
    62  	for _, objs := range vsom.Catalog() {
    63  		for id, val := range objs {
    64  			if id.Id == details.BackingDiskId {
    65  				return &val.VStorageObject, nil
    66  			}
    67  		}
    68  	}
    69  
    70  	return nil, &vim25types.InvalidArgument{InvalidProperty: "VolumeId"}
    71  }
    72  
    73  func (m *CnsVolumeManager) createDisk(vctx *simulator.Context, createSpec cnstypes.CnsVolumeCreateSpec) (*vim25types.VStorageObject, vim25types.BaseMethodFault) {
    74  	vsom := vctx.Map.VStorageObjectManager()
    75  
    76  	if len(createSpec.Datastores) == 0 {
    77  		return nil, &vim25types.InvalidArgument{InvalidProperty: "createSpecs.datastores"}
    78  	}
    79  
    80  	// "Datastores to be considered for volume placement" - we'll just use the 1st for now
    81  	datastoreRef := createSpec.Datastores[0]
    82  
    83  	val := vsom.CreateDiskTask(vctx, &vim25types.CreateDisk_Task{
    84  		This: vsom.Self,
    85  		Spec: vim25types.VslmCreateSpec{
    86  			Name:              createSpec.Name,
    87  			KeepAfterDeleteVm: vim25types.NewBool(true),
    88  			CapacityInMB:      createSpec.BackingObjectDetails.GetCnsBackingObjectDetails().CapacityInMb,
    89  			Profile:           createSpec.Profile,
    90  			BackingSpec: &vim25types.VslmCreateSpecDiskFileBackingSpec{
    91  				VslmCreateSpecBackingSpec: vim25types.VslmCreateSpecBackingSpec{
    92  					Datastore: datastoreRef,
    93  				},
    94  				ProvisioningType: string(vim25types.BaseConfigInfoDiskFileBackingInfoProvisioningTypeThin),
    95  			},
    96  		},
    97  	})
    98  
    99  	ref := val.(*vim25methods.CreateDisk_TaskBody).Res.Returnval
   100  	task := vctx.Map.Get(ref).(*simulator.Task)
   101  	task.Wait()
   102  	if task.Info.Error != nil {
   103  		return nil, task.Info.Error.Fault
   104  	}
   105  
   106  	return task.Info.Result.(*vim25types.VStorageObject), nil
   107  }
   108  
   109  func (m *CnsVolumeManager) CnsCreateVolume(ctx *simulator.Context, req *cnstypes.CnsCreateVolume) soap.HasFault {
   110  	vctx := ctx.For(vim25.Path)
   111  	vsom := vctx.Map.VStorageObjectManager()
   112  
   113  	task := simulator.CreateTask(m, "CnsCreateVolume", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
   114  		if len(req.CreateSpecs) != 1 {
   115  			return nil, &vim25types.InvalidArgument{InvalidProperty: "InputSpec"} // Same as real VC, currently
   116  		}
   117  
   118  		var obj *vim25types.VStorageObject
   119  		var fault vim25types.BaseMethodFault
   120  
   121  		createSpec := req.CreateSpecs[0]
   122  
   123  		switch details := createSpec.BackingObjectDetails.(type) {
   124  		case *cnstypes.CnsBlockBackingDetails:
   125  			if details.BackingDiskId == "" {
   126  				obj, fault = m.createDisk(vctx, createSpec)
   127  			} else {
   128  				obj, fault = m.findDisk(vctx, createSpec)
   129  			}
   130  			if fault != nil {
   131  				return nil, fault
   132  			}
   133  		default:
   134  			return nil, &vim25types.InvalidArgument{InvalidProperty: "createSpecs.backingObjectDetails"}
   135  		}
   136  
   137  		datastoreRef := obj.Config.Backing.GetBaseConfigInfoBackingInfo().Datastore
   138  		datastore := vctx.Map.Get(datastoreRef).(*simulator.Datastore)
   139  
   140  		volumes, ok := m.volumes[datastore.Self]
   141  		if !ok {
   142  			volumes = make(map[cnstypes.CnsVolumeId]*cnstypes.CnsVolume)
   143  			m.volumes[datastore.Self] = volumes
   144  		}
   145  
   146  		policyId := ""
   147  		if len(createSpec.Profile) != 0 {
   148  			if profileSpec, ok := createSpec.Profile[0].(*vim25types.VirtualMachineDefinedProfileSpec); ok {
   149  				policyId = profileSpec.ProfileId
   150  			}
   151  		}
   152  
   153  		volume := &cnstypes.CnsVolume{
   154  			VolumeId:     cnstypes.CnsVolumeId(obj.Config.Id),
   155  			Name:         createSpec.Name,
   156  			VolumeType:   createSpec.VolumeType,
   157  			DatastoreUrl: datastore.Info.GetDatastoreInfo().Url,
   158  			Metadata:     createSpec.Metadata,
   159  			BackingObjectDetails: &cnstypes.CnsBlockBackingDetails{
   160  				CnsBackingObjectDetails: cnstypes.CnsBackingObjectDetails{
   161  					CapacityInMb: obj.Config.CapacityInMB,
   162  				},
   163  				BackingDiskId:   obj.Config.Id.Id,
   164  				BackingDiskPath: obj.Config.Backing.(*vim25types.BaseConfigInfoDiskFileBackingInfo).FilePath,
   165  			},
   166  			ComplianceStatus:             string(pbmtypes.PbmComplianceStatusCompliant),
   167  			DatastoreAccessibilityStatus: string(pbmtypes.PbmHealthStatusForEntityGreen),
   168  			HealthStatus:                 string(pbmtypes.PbmHealthStatusForEntityGreen),
   169  			StoragePolicyId:              policyId,
   170  		}
   171  
   172  		volumes[volume.VolumeId] = volume
   173  
   174  		operationResult := []cnstypes.BaseCnsVolumeOperationResult{
   175  			&cnstypes.CnsVolumeCreateResult{
   176  				CnsVolumeOperationResult: cnstypes.CnsVolumeOperationResult{
   177  					VolumeId: volume.VolumeId,
   178  				},
   179  				Name: volume.Name,
   180  				PlacementResults: []cnstypes.CnsPlacementResult{{
   181  					Datastore: datastore.Reference(),
   182  				}},
   183  			},
   184  		}
   185  
   186  		cc := createSpec.Metadata.ContainerCluster
   187  		vsom.VCenterUpdateVStorageObjectMetadataExTask(vctx, &vim25types.VCenterUpdateVStorageObjectMetadataEx_Task{
   188  			This:      vsom.Self,
   189  			Id:        obj.Config.Id,
   190  			Datastore: datastore.Self,
   191  			Metadata: []vim25types.KeyValue{
   192  				{Key: "cns.containerCluster.clusterDistribution", Value: cc.ClusterDistribution},
   193  				{Key: "cns.containerCluster.clusterFlavor", Value: cc.ClusterFlavor},
   194  				{Key: "cns.containerCluster.clusterId", Value: cc.ClusterId},
   195  				{Key: "cns.containerCluster.vSphereUser", Value: cc.VSphereUser},
   196  			},
   197  		})
   198  
   199  		return &cnstypes.CnsVolumeOperationBatchResult{
   200  			VolumeResults: operationResult,
   201  		}, nil
   202  	})
   203  
   204  	return &methods.CnsCreateVolumeBody{
   205  		Res: &cnstypes.CnsCreateVolumeResponse{
   206  			Returnval: task.Run(vctx),
   207  		},
   208  	}
   209  }
   210  
   211  func matchesDatastore(filter cnstypes.CnsQueryFilter, ds vim25types.ManagedObjectReference) bool {
   212  	if len(filter.Datastores) == 0 {
   213  		return true
   214  	}
   215  	return slices.Contains(filter.Datastores, ds)
   216  }
   217  
   218  func matchesID(filter cnstypes.CnsQueryFilter, volume *types.CnsVolume) bool {
   219  	if len(filter.VolumeIds) == 0 {
   220  		return true
   221  	}
   222  	return slices.Contains(filter.VolumeIds, volume.VolumeId)
   223  }
   224  
   225  func matchesName(filter cnstypes.CnsQueryFilter, volume *types.CnsVolume) bool {
   226  	if len(filter.Names) == 0 {
   227  		return true
   228  	}
   229  	return slices.Contains(filter.Names, volume.Name)
   230  }
   231  
   232  func matchesContainerCluster(filter cnstypes.CnsQueryFilter, volume *types.CnsVolume) bool {
   233  	if len(filter.ContainerClusterIds) == 0 {
   234  		return true
   235  	}
   236  	return slices.Contains(filter.ContainerClusterIds, volume.Metadata.ContainerCluster.ClusterId)
   237  }
   238  
   239  func matchesStoragePolicy(filter cnstypes.CnsQueryFilter, volume *types.CnsVolume) bool {
   240  	return filter.StoragePolicyId == "" || filter.StoragePolicyId == volume.StoragePolicyId
   241  }
   242  
   243  func matchesLabel(filter cnstypes.CnsQueryFilter, volume *types.CnsVolume) bool {
   244  	if len(filter.Labels) == 0 {
   245  		return true
   246  	}
   247  
   248  	for _, meta := range volume.Metadata.EntityMetadata {
   249  		for _, label := range meta.GetCnsEntityMetadata().Labels {
   250  			if slices.Contains(filter.Labels, label) {
   251  				return true
   252  			}
   253  		}
   254  	}
   255  
   256  	return false
   257  }
   258  
   259  func matchesComplianceStatus(filter cnstypes.CnsQueryFilter, volume *types.CnsVolume) bool {
   260  	return filter.ComplianceStatus == "" || filter.ComplianceStatus == volume.ComplianceStatus
   261  }
   262  
   263  func matchesHealth(filter cnstypes.CnsQueryFilter, volume *types.CnsVolume) bool {
   264  	return filter.HealthStatus == "" || filter.HealthStatus == volume.HealthStatus
   265  }
   266  
   267  func matchesFilter(filter cnstypes.CnsQueryFilter, volume *types.CnsVolume) bool {
   268  	matches := []func(cnstypes.CnsQueryFilter, *types.CnsVolume) bool{
   269  		matchesID,
   270  		matchesName,
   271  		matchesContainerCluster,
   272  		matchesStoragePolicy,
   273  		matchesLabel,
   274  		matchesComplianceStatus,
   275  		matchesHealth,
   276  	}
   277  
   278  	for _, match := range matches {
   279  		if !match(filter, volume) {
   280  			return false
   281  		}
   282  	}
   283  
   284  	return true
   285  }
   286  
   287  func (m *CnsVolumeManager) queryVolume(filter cnstypes.CnsQueryFilter) []cnstypes.CnsVolume {
   288  	var matches []cnstypes.CnsVolume
   289  
   290  	for ds, volumes := range m.volumes {
   291  		if !matchesDatastore(filter, ds) {
   292  			continue
   293  		}
   294  		for _, volume := range volumes {
   295  			if matchesFilter(filter, volume) {
   296  				matches = append(matches, *volume)
   297  			}
   298  		}
   299  	}
   300  
   301  	return matches
   302  }
   303  
   304  // CnsQueryVolume simulates the query volumes implementation for CNSQuery API
   305  func (m *CnsVolumeManager) CnsQueryVolume(ctx context.Context, req *cnstypes.CnsQueryVolume) soap.HasFault {
   306  	// TODO: paginate results using CnsCursor
   307  	return &methods.CnsQueryVolumeBody{
   308  		Res: &cnstypes.CnsQueryVolumeResponse{
   309  			Returnval: cnstypes.CnsQueryResult{
   310  				Volumes: m.queryVolume(req.Filter),
   311  			},
   312  		},
   313  	}
   314  }
   315  
   316  // CnsQueryAllVolume simulates the query volumes implementation for CNSQueryAll API
   317  func (m *CnsVolumeManager) CnsQueryAllVolume(ctx context.Context, req *cnstypes.CnsQueryAllVolume) soap.HasFault {
   318  	// Note we ignore req.Selection, which can limit fields to reduce response size
   319  	// This method is internal and does not paginate results using CnsCursor.
   320  	return &methods.CnsQueryAllVolumeBody{
   321  		Res: &cnstypes.CnsQueryAllVolumeResponse{
   322  			Returnval: cnstypes.CnsQueryResult{
   323  				Volumes: m.queryVolume(req.Filter),
   324  			},
   325  		},
   326  	}
   327  }
   328  
   329  func (m *CnsVolumeManager) CnsDeleteVolume(ctx *simulator.Context, req *cnstypes.CnsDeleteVolume) soap.HasFault {
   330  	vctx := ctx.For(vim25.Path)
   331  	vsom := vctx.Map.VStorageObjectManager()
   332  
   333  	task := simulator.CreateTask(m, "CnsDeleteVolume", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
   334  		if len(req.VolumeIds) != 1 {
   335  			return nil, &vim25types.InvalidArgument{InvalidProperty: "InputSpec"} // Same as real VC, currently
   336  		}
   337  
   338  		found := false
   339  		volumeId := req.VolumeIds[0]
   340  		res := &cnstypes.CnsVolumeOperationResult{
   341  			VolumeId: volumeId,
   342  		}
   343  
   344  		for ds, volumes := range m.volumes {
   345  			if _, ok := volumes[volumeId]; ok {
   346  				found = true
   347  				delete(m.volumes[ds], volumeId)
   348  
   349  				if req.DeleteDisk {
   350  					val := vsom.DeleteVStorageObjectTask(vctx, &vim25types.DeleteVStorageObject_Task{
   351  						This:      vsom.Self,
   352  						Id:        vim25types.ID(volumeId),
   353  						Datastore: ds,
   354  					})
   355  
   356  					ref := val.(*vim25methods.DeleteVStorageObject_TaskBody).Res.Returnval
   357  					task := vctx.Map.Get(ref).(*simulator.Task)
   358  					task.Wait()
   359  					if task.Info.Error != nil {
   360  						res.Fault = task.Info.Error
   361  					}
   362  				}
   363  			}
   364  		}
   365  
   366  		if !found {
   367  			res.Fault = &vim25types.LocalizedMethodFault{Fault: new(vim25types.NotFound)}
   368  		}
   369  
   370  		return &cnstypes.CnsVolumeOperationBatchResult{
   371  			VolumeResults: []cnstypes.BaseCnsVolumeOperationResult{res},
   372  		}, nil
   373  	})
   374  
   375  	return &methods.CnsDeleteVolumeBody{
   376  		Res: &cnstypes.CnsDeleteVolumeResponse{
   377  			Returnval: task.Run(vctx),
   378  		},
   379  	}
   380  }
   381  
   382  // CnsUpdateVolumeMetadata simulates UpdateVolumeMetadata call for simulated vc
   383  func (m *CnsVolumeManager) CnsUpdateVolumeMetadata(ctx *simulator.Context, req *cnstypes.CnsUpdateVolumeMetadata) soap.HasFault {
   384  	task := simulator.CreateTask(m, "CnsUpdateVolumeMetadata", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
   385  		if len(req.UpdateSpecs) == 0 {
   386  			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsUpdateVolumeMetadataSpec"}
   387  		}
   388  		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
   389  		for _, updateSpecs := range req.UpdateSpecs {
   390  			for _, dsVolumes := range m.volumes {
   391  				for id, volume := range dsVolumes {
   392  					if id.Id == updateSpecs.VolumeId.Id {
   393  						volume.Metadata.EntityMetadata = updateSpecs.Metadata.EntityMetadata
   394  						operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
   395  							VolumeId: volume.VolumeId,
   396  						})
   397  						break
   398  					}
   399  				}
   400  			}
   401  
   402  		}
   403  		return &cnstypes.CnsVolumeOperationBatchResult{
   404  			VolumeResults: operationResult,
   405  		}, nil
   406  	})
   407  	return &methods.CnsUpdateVolumeBody{
   408  		Res: &cnstypes.CnsUpdateVolumeMetadataResponse{
   409  			Returnval: task.Run(ctx),
   410  		},
   411  	}
   412  }
   413  
   414  // CnsAttachVolume simulates AttachVolume call for simulated vc
   415  func (m *CnsVolumeManager) CnsAttachVolume(ctx *simulator.Context, req *cnstypes.CnsAttachVolume) soap.HasFault {
   416  	vctx := ctx.For(vim25.Path)
   417  	task := simulator.CreateTask(m, "CnsAttachVolume", func(task *simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
   418  		if len(req.AttachSpecs) == 0 {
   419  			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsAttachVolumeSpec"}
   420  		}
   421  		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
   422  		for _, attachSpec := range req.AttachSpecs {
   423  			node := vctx.Map.Get(attachSpec.Vm).(*simulator.VirtualMachine)
   424  			if _, ok := m.attachments[attachSpec.VolumeId]; !ok {
   425  				m.attachments[attachSpec.VolumeId] = node.Self
   426  			} else {
   427  				return nil, &vim25types.ResourceInUse{
   428  					Name: attachSpec.VolumeId.Id,
   429  				}
   430  			}
   431  			operationResult = append(operationResult, &cnstypes.CnsVolumeAttachResult{
   432  				CnsVolumeOperationResult: cnstypes.CnsVolumeOperationResult{
   433  					VolumeId: attachSpec.VolumeId,
   434  				},
   435  				DiskUUID: simulatorDiskUUID,
   436  			})
   437  		}
   438  
   439  		return &cnstypes.CnsVolumeOperationBatchResult{
   440  			VolumeResults: operationResult,
   441  		}, nil
   442  	})
   443  
   444  	return &methods.CnsAttachVolumeBody{
   445  		Res: &cnstypes.CnsAttachVolumeResponse{
   446  			Returnval: task.Run(vctx),
   447  		},
   448  	}
   449  }
   450  
   451  // CnsDetachVolume simulates DetachVolume call for simulated vc
   452  func (m *CnsVolumeManager) CnsDetachVolume(ctx *simulator.Context, req *cnstypes.CnsDetachVolume) soap.HasFault {
   453  	task := simulator.CreateTask(m, "CnsDetachVolume", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
   454  		if len(req.DetachSpecs) == 0 {
   455  			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsDetachVolumeSpec"}
   456  		}
   457  		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
   458  		for _, detachSpec := range req.DetachSpecs {
   459  			if _, ok := m.attachments[detachSpec.VolumeId]; ok {
   460  				delete(m.attachments, detachSpec.VolumeId)
   461  				operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
   462  					VolumeId: detachSpec.VolumeId,
   463  				})
   464  			} else {
   465  				return nil, &vim25types.InvalidArgument{
   466  					InvalidProperty: detachSpec.VolumeId.Id,
   467  				}
   468  			}
   469  		}
   470  
   471  		return &cnstypes.CnsVolumeOperationBatchResult{
   472  			VolumeResults: operationResult,
   473  		}, nil
   474  	})
   475  	return &methods.CnsDetachVolumeBody{
   476  		Res: &cnstypes.CnsDetachVolumeResponse{
   477  			Returnval: task.Run(ctx),
   478  		},
   479  	}
   480  }
   481  
   482  // CnsExtendVolume simulates ExtendVolume call for simulated vc
   483  func (m *CnsVolumeManager) CnsExtendVolume(ctx *simulator.Context, req *cnstypes.CnsExtendVolume) soap.HasFault {
   484  	task := simulator.CreateTask(m, "CnsExtendVolume", func(task *simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
   485  		if len(req.ExtendSpecs) != 1 {
   486  			return nil, &vim25types.InvalidArgument{InvalidProperty: "InputSpec"} // Same as real VC, currently
   487  		}
   488  
   489  		found := false
   490  		spec := req.ExtendSpecs[0]
   491  		res := cnstypes.CnsVolumeOperationResult{
   492  			VolumeId: spec.VolumeId,
   493  		}
   494  
   495  		for _, volumes := range m.volumes {
   496  			if volume, ok := volumes[spec.VolumeId]; ok {
   497  				found = true
   498  				volume.BackingObjectDetails.GetCnsBackingObjectDetails().CapacityInMb = spec.CapacityInMb
   499  				break
   500  			}
   501  		}
   502  
   503  		if !found {
   504  			res.Fault = &vim25types.LocalizedMethodFault{Fault: new(vim25types.NotFound)}
   505  		}
   506  
   507  		return &cnstypes.CnsVolumeOperationBatchResult{
   508  			VolumeResults: []cnstypes.BaseCnsVolumeOperationResult{&res},
   509  		}, nil
   510  	})
   511  
   512  	return &methods.CnsExtendVolumeBody{
   513  		Res: &cnstypes.CnsExtendVolumeResponse{
   514  			Returnval: task.Run(ctx),
   515  		},
   516  	}
   517  }
   518  
   519  func (m *CnsVolumeManager) CnsQueryVolumeInfo(ctx *simulator.Context, req *cnstypes.CnsQueryVolumeInfo) soap.HasFault {
   520  	vctx := ctx.For(vim25.Path)
   521  	vsom := vctx.Map.VStorageObjectManager()
   522  
   523  	task := simulator.CreateTask(m, "CnsQueryVolumeInfo", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
   524  		var operationResult []cnstypes.BaseCnsVolumeOperationResult
   525  
   526  		for _, objs := range vsom.Catalog() {
   527  			for _, obj := range objs {
   528  				for _, volumeId := range req.VolumeIds {
   529  					if obj.Config.Id.Id == volumeId.Id {
   530  						operationResult = append(operationResult, &cnstypes.CnsQueryVolumeInfoResult{
   531  							CnsVolumeOperationResult: cnstypes.CnsVolumeOperationResult{
   532  								VolumeId: volumeId,
   533  							},
   534  							VolumeInfo: &cnstypes.CnsBlockVolumeInfo{
   535  								VStorageObject: obj.VStorageObject,
   536  							},
   537  						})
   538  					}
   539  				}
   540  			}
   541  		}
   542  
   543  		return &cnstypes.CnsVolumeOperationBatchResult{
   544  			VolumeResults: operationResult,
   545  		}, nil
   546  	})
   547  
   548  	return &methods.CnsQueryVolumeInfoBody{
   549  		Res: &cnstypes.CnsQueryVolumeInfoResponse{
   550  			Returnval: task.Run(vctx),
   551  		},
   552  	}
   553  }
   554  
   555  func (m *CnsVolumeManager) CnsQueryAsync(ctx *simulator.Context, req *cnstypes.CnsQueryAsync) soap.HasFault {
   556  	task := simulator.CreateTask(m, "QueryVolumeAsync", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
   557  		retVolumes := []cnstypes.CnsVolume{}
   558  		reqVolumeIds := make(map[string]bool)
   559  		isQueryFilter := false
   560  
   561  		if req.Filter.VolumeIds != nil {
   562  			isQueryFilter = true
   563  		}
   564  		// Create map of requested volume Ids in query request
   565  		for _, volumeID := range req.Filter.VolumeIds {
   566  			reqVolumeIds[volumeID.Id] = true
   567  		}
   568  
   569  		for _, dsVolumes := range m.volumes {
   570  			for _, volume := range dsVolumes {
   571  				if isQueryFilter {
   572  					if _, ok := reqVolumeIds[volume.VolumeId.Id]; ok {
   573  						retVolumes = append(retVolumes, *volume)
   574  					}
   575  				} else {
   576  					retVolumes = append(retVolumes, *volume)
   577  				}
   578  			}
   579  		}
   580  		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
   581  		operationResult = append(operationResult, &cnstypes.CnsAsyncQueryResult{
   582  			QueryResult: cnstypes.CnsQueryResult{
   583  				Volumes: retVolumes,
   584  				Cursor:  cnstypes.CnsCursor{},
   585  			},
   586  		})
   587  
   588  		return &cnstypes.CnsVolumeOperationBatchResult{
   589  			VolumeResults: operationResult,
   590  		}, nil
   591  	})
   592  
   593  	return &methods.CnsQueryAsyncBody{
   594  		Res: &cnstypes.CnsQueryAsyncResponse{
   595  			Returnval: task.Run(ctx),
   596  		},
   597  	}
   598  }
   599  
   600  func (m *CnsVolumeManager) CnsCreateSnapshots(ctx *simulator.Context, req *cnstypes.CnsCreateSnapshots) soap.HasFault {
   601  	task := simulator.CreateTask(m, "CreateSnapshots", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
   602  		if len(req.SnapshotSpecs) == 0 {
   603  			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsSnapshotCreateSpec"}
   604  		}
   605  
   606  		snapshotOperationResult := []cnstypes.BaseCnsVolumeOperationResult{}
   607  		for _, snapshotCreateSpec := range req.SnapshotSpecs {
   608  			for _, dsVolumes := range m.volumes {
   609  				for id := range dsVolumes {
   610  					if id.Id != snapshotCreateSpec.VolumeId.Id {
   611  						continue
   612  					}
   613  					snapshots, ok := m.snapshots[snapshotCreateSpec.VolumeId]
   614  					if !ok {
   615  						snapshots = make(map[cnstypes.CnsSnapshotId]*cnstypes.CnsSnapshot)
   616  						m.snapshots[snapshotCreateSpec.VolumeId] = snapshots
   617  					}
   618  
   619  					newSnapshot := &cnstypes.CnsSnapshot{
   620  						SnapshotId: cnstypes.CnsSnapshotId{
   621  							Id: uuid.New().String(),
   622  						},
   623  						VolumeId:    snapshotCreateSpec.VolumeId,
   624  						Description: snapshotCreateSpec.Description,
   625  						CreateTime:  time.Now(),
   626  					}
   627  					snapshots[newSnapshot.SnapshotId] = newSnapshot
   628  					snapshotOperationResult = append(snapshotOperationResult, &cnstypes.CnsSnapshotCreateResult{
   629  						CnsSnapshotOperationResult: cnstypes.CnsSnapshotOperationResult{
   630  							CnsVolumeOperationResult: cnstypes.CnsVolumeOperationResult{
   631  								VolumeId: newSnapshot.VolumeId,
   632  							},
   633  						},
   634  						Snapshot: *newSnapshot,
   635  					})
   636  				}
   637  			}
   638  		}
   639  
   640  		return &cnstypes.CnsVolumeOperationBatchResult{
   641  			VolumeResults: snapshotOperationResult,
   642  		}, nil
   643  	})
   644  
   645  	return &methods.CnsCreateSnapshotsBody{
   646  		Res: &cnstypes.CnsCreateSnapshotsResponse{
   647  			Returnval: task.Run(ctx),
   648  		},
   649  	}
   650  }
   651  
   652  func (m *CnsVolumeManager) CnsDeleteSnapshots(ctx *simulator.Context, req *cnstypes.CnsDeleteSnapshots) soap.HasFault {
   653  	task := simulator.CreateTask(m, "DeleteSnapshots", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
   654  		snapshotOperationResult := []cnstypes.BaseCnsVolumeOperationResult{}
   655  		for _, snapshotDeleteSpec := range req.SnapshotDeleteSpecs {
   656  			for _, dsVolumes := range m.volumes {
   657  				for id := range dsVolumes {
   658  					if id.Id != snapshotDeleteSpec.VolumeId.Id {
   659  						continue
   660  					}
   661  					snapshots := m.snapshots[snapshotDeleteSpec.VolumeId]
   662  					snapshot, ok := snapshots[snapshotDeleteSpec.SnapshotId]
   663  					if ok {
   664  						delete(m.snapshots[snapshotDeleteSpec.VolumeId], snapshotDeleteSpec.SnapshotId)
   665  						snapshotOperationResult = append(snapshotOperationResult, &cnstypes.CnsSnapshotDeleteResult{
   666  							CnsSnapshotOperationResult: cnstypes.CnsSnapshotOperationResult{
   667  								CnsVolumeOperationResult: cnstypes.CnsVolumeOperationResult{
   668  									VolumeId: snapshot.VolumeId,
   669  								},
   670  							},
   671  							SnapshotId: snapshot.SnapshotId,
   672  						})
   673  					}
   674  				}
   675  			}
   676  		}
   677  
   678  		return &cnstypes.CnsVolumeOperationBatchResult{
   679  			VolumeResults: snapshotOperationResult,
   680  		}, nil
   681  	})
   682  
   683  	return &methods.CnsDeleteSnapshotBody{
   684  		Res: &cnstypes.CnsDeleteSnapshotsResponse{
   685  			Returnval: task.Run(ctx),
   686  		},
   687  	}
   688  }
   689  
   690  func (m *CnsVolumeManager) CnsQuerySnapshots(ctx *simulator.Context, req *cnstypes.CnsQuerySnapshots) soap.HasFault {
   691  	task := simulator.CreateTask(m, "QuerySnapshots", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
   692  		if len(req.SnapshotQueryFilter.SnapshotQuerySpecs) > 1 {
   693  			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsSnapshotQuerySpec"}
   694  		}
   695  
   696  		snapshotQueryResultEntries := []cnstypes.CnsSnapshotQueryResultEntry{}
   697  		checkVolumeExists := func(volumeId cnstypes.CnsVolumeId) bool {
   698  			for _, dsVolumes := range m.volumes {
   699  				for id := range dsVolumes {
   700  					if id.Id == volumeId.Id {
   701  						return true
   702  					}
   703  				}
   704  			}
   705  			return false
   706  		}
   707  
   708  		if req.SnapshotQueryFilter.SnapshotQuerySpecs == nil && len(req.SnapshotQueryFilter.SnapshotQuerySpecs) == 0 {
   709  			// return all snapshots if snapshotQuerySpecs is empty
   710  			for _, volSnapshots := range m.snapshots {
   711  				for _, snapshot := range volSnapshots {
   712  					snapshotQueryResultEntries = append(snapshotQueryResultEntries, cnstypes.CnsSnapshotQueryResultEntry{Snapshot: *snapshot})
   713  				}
   714  			}
   715  		} else {
   716  			// snapshotQuerySpecs is not empty
   717  			isSnapshotQueryFilter := false
   718  			snapshotQuerySpec := req.SnapshotQueryFilter.SnapshotQuerySpecs[0]
   719  			if snapshotQuerySpec.SnapshotId != nil && (*snapshotQuerySpec.SnapshotId != cnstypes.CnsSnapshotId{}) {
   720  				isSnapshotQueryFilter = true
   721  			}
   722  
   723  			if !checkVolumeExists(snapshotQuerySpec.VolumeId) {
   724  				// volumeId in snapshotQuerySpecs does not exist
   725  				snapshotQueryResultEntries = append(snapshotQueryResultEntries, cnstypes.CnsSnapshotQueryResultEntry{
   726  					Error: &vim25types.LocalizedMethodFault{
   727  						Fault: cnstypes.CnsVolumeNotFoundFault{
   728  							VolumeId: snapshotQuerySpec.VolumeId,
   729  						},
   730  					},
   731  				})
   732  			} else {
   733  				// volumeId in snapshotQuerySpecs exists
   734  				for _, snapshot := range m.snapshots[snapshotQuerySpec.VolumeId] {
   735  					if isSnapshotQueryFilter && snapshot.SnapshotId.Id != (*snapshotQuerySpec.SnapshotId).Id {
   736  						continue
   737  					}
   738  
   739  					snapshotQueryResultEntries = append(snapshotQueryResultEntries, cnstypes.CnsSnapshotQueryResultEntry{Snapshot: *snapshot})
   740  				}
   741  
   742  				if isSnapshotQueryFilter && len(snapshotQueryResultEntries) == 0 {
   743  					snapshotQueryResultEntries = append(snapshotQueryResultEntries, cnstypes.CnsSnapshotQueryResultEntry{
   744  						Error: &vim25types.LocalizedMethodFault{
   745  							Fault: cnstypes.CnsSnapshotNotFoundFault{
   746  								VolumeId:   snapshotQuerySpec.VolumeId,
   747  								SnapshotId: *snapshotQuerySpec.SnapshotId,
   748  							},
   749  						},
   750  					})
   751  				}
   752  			}
   753  		}
   754  
   755  		return &cnstypes.CnsSnapshotQueryResult{
   756  			Entries: snapshotQueryResultEntries,
   757  		}, nil
   758  	})
   759  
   760  	return &methods.CnsQuerySnapshotsBody{
   761  		Res: &cnstypes.CnsQuerySnapshotsResponse{
   762  			Returnval: task.Run(ctx),
   763  		},
   764  	}
   765  }