github.com/vmware/govmomi@v0.51.0/simulator/vstorage_object_manager.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  	"log"
     9  	"net/url"
    10  	"os"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/google/uuid"
    15  
    16  	"github.com/vmware/govmomi/object"
    17  	"github.com/vmware/govmomi/vim25/methods"
    18  	"github.com/vmware/govmomi/vim25/mo"
    19  	"github.com/vmware/govmomi/vim25/soap"
    20  	"github.com/vmware/govmomi/vim25/types"
    21  )
    22  
    23  type VStorageObject struct {
    24  	types.VStorageObject
    25  	types.VStorageObjectSnapshotInfo
    26  	Metadata []types.KeyValue
    27  }
    28  
    29  type VcenterVStorageObjectManager struct {
    30  	mo.VcenterVStorageObjectManager
    31  
    32  	objects map[types.ManagedObjectReference]map[types.ID]*VStorageObject
    33  }
    34  
    35  func (m *VcenterVStorageObjectManager) init(*Registry) {
    36  	m.objects = make(map[types.ManagedObjectReference]map[types.ID]*VStorageObject)
    37  }
    38  
    39  func (m *VcenterVStorageObjectManager) object(ds types.ManagedObjectReference, id types.ID) *VStorageObject {
    40  	if objects, ok := m.objects[ds]; ok {
    41  		return objects[id]
    42  	}
    43  	return nil
    44  }
    45  
    46  func (m *VcenterVStorageObjectManager) Catalog() map[types.ManagedObjectReference]map[types.ID]*VStorageObject {
    47  	return m.objects
    48  }
    49  
    50  func (m *VcenterVStorageObjectManager) ListVStorageObject(req *types.ListVStorageObject) soap.HasFault {
    51  	body := &methods.ListVStorageObjectBody{
    52  		Res: &types.ListVStorageObjectResponse{},
    53  	}
    54  
    55  	if objects, ok := m.objects[req.Datastore]; ok {
    56  		for id := range objects {
    57  			body.Res.Returnval = append(body.Res.Returnval, id)
    58  		}
    59  	}
    60  
    61  	return body
    62  }
    63  
    64  func (m *VcenterVStorageObjectManager) RetrieveVStorageObject(ctx *Context, req *types.RetrieveVStorageObject) soap.HasFault {
    65  	body := new(methods.RetrieveVStorageObjectBody)
    66  
    67  	obj := m.object(req.Datastore, req.Id)
    68  	if obj == nil {
    69  		body.Fault_ = Fault("", new(types.NotFound))
    70  	} else {
    71  		stat := m.statDatastoreBacking(ctx, req.Datastore, &req.Id)
    72  		if err := stat[req.Id]; err != nil {
    73  			body.Fault_ = Fault(err.Error(), new(types.NotFound))
    74  			return body
    75  		}
    76  		body.Res = &types.RetrieveVStorageObjectResponse{
    77  			Returnval: obj.VStorageObject,
    78  		}
    79  	}
    80  
    81  	return body
    82  }
    83  
    84  // statDatastoreBacking checks if object(s) backing file exists on the given datastore ref.
    85  func (m *VcenterVStorageObjectManager) statDatastoreBacking(ctx *Context, ref types.ManagedObjectReference, id *types.ID) map[types.ID]error {
    86  	objs := m.objects[ref] // default to checking all objects
    87  	if id != nil {
    88  		// check for a specific object
    89  		objs = map[types.ID]*VStorageObject{
    90  			*id: objs[*id],
    91  		}
    92  	}
    93  	res := make(map[types.ID]error, len(objs))
    94  	ds := ctx.Map.Get(ref).(*Datastore)
    95  	dc := ctx.Map.getEntityDatacenter(ds)
    96  	fm := ctx.Map.FileManager()
    97  
    98  	for _, obj := range objs {
    99  		backing := obj.Config.Backing.(*types.BaseConfigInfoDiskFileBackingInfo)
   100  		file, _ := fm.resolve(ctx, &dc.Self, backing.FilePath)
   101  		_, res[obj.Config.Id] = os.Stat(file)
   102  	}
   103  
   104  	return res
   105  }
   106  
   107  func (m *VcenterVStorageObjectManager) ReconcileDatastoreInventoryTask(ctx *Context, req *types.ReconcileDatastoreInventory_Task) soap.HasFault {
   108  	task := CreateTask(m, "reconcileDatastoreInventory", func(*Task) (types.AnyType, types.BaseMethodFault) {
   109  		objs := m.objects[req.Datastore]
   110  		stat := m.statDatastoreBacking(ctx, req.Datastore, nil)
   111  
   112  		for id, err := range stat {
   113  			if os.IsNotExist(err) {
   114  				log.Printf("removing disk %s from inventory: %s", id.Id, err)
   115  				delete(objs, id)
   116  			}
   117  		}
   118  
   119  		return nil, nil
   120  	})
   121  
   122  	return &methods.ReconcileDatastoreInventory_TaskBody{
   123  		Res: &types.ReconcileDatastoreInventory_TaskResponse{
   124  			Returnval: task.Run(ctx),
   125  		},
   126  	}
   127  }
   128  
   129  func (m *VcenterVStorageObjectManager) RegisterDisk(ctx *Context, req *types.RegisterDisk) soap.HasFault {
   130  	body := new(methods.RegisterDiskBody)
   131  
   132  	invalid := func() soap.HasFault {
   133  		body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "path"})
   134  		return body
   135  	}
   136  
   137  	u, err := url.Parse(req.Path)
   138  	if err != nil {
   139  		return invalid()
   140  	}
   141  	u.Path = strings.TrimPrefix(u.Path, folderPrefix)
   142  
   143  	ds, err := ctx.svc.findDatastore(u.Query())
   144  	if err != nil {
   145  		return invalid()
   146  	}
   147  
   148  	st, err := os.Stat(ds.resolve(ctx, u.Path))
   149  	if err != nil {
   150  		return invalid()
   151  	}
   152  	if st.IsDir() {
   153  		return invalid()
   154  	}
   155  
   156  	path := (&object.DatastorePath{Datastore: ds.Name, Path: u.Path}).String()
   157  
   158  	for _, obj := range m.objects[ds.Self] {
   159  		backing := obj.Config.Backing.(*types.BaseConfigInfoDiskFileBackingInfo)
   160  		if backing.FilePath == path {
   161  			return invalid()
   162  		}
   163  	}
   164  
   165  	creq := &types.CreateDisk_Task{
   166  		Spec: types.VslmCreateSpec{
   167  			Name: req.Name,
   168  			BackingSpec: &types.VslmCreateSpecDiskFileBackingSpec{
   169  				VslmCreateSpecBackingSpec: types.VslmCreateSpecBackingSpec{
   170  					Datastore: ds.Self,
   171  					Path:      u.Path,
   172  				},
   173  			},
   174  		},
   175  	}
   176  
   177  	obj, fault := m.createObject(ctx, creq, true)
   178  	if fault != nil {
   179  		body.Fault_ = Fault("", fault)
   180  		return body
   181  	}
   182  
   183  	body.Res = &types.RegisterDiskResponse{
   184  		Returnval: *obj,
   185  	}
   186  
   187  	return body
   188  }
   189  
   190  func (m *VcenterVStorageObjectManager) createObject(ctx *Context, req *types.CreateDisk_Task, register bool) (*types.VStorageObject, types.BaseMethodFault) {
   191  	dir := "fcd"
   192  	ref := req.Spec.BackingSpec.GetVslmCreateSpecBackingSpec().Datastore
   193  	ds := ctx.Map.Get(ref).(*Datastore)
   194  	dc := ctx.Map.getEntityDatacenter(ds)
   195  
   196  	objects, ok := m.objects[ds.Self]
   197  	if !ok {
   198  		objects = make(map[types.ID]*VStorageObject)
   199  		m.objects[ds.Self] = objects
   200  		_ = os.MkdirAll(ds.resolve(ctx, dir), 0750)
   201  	}
   202  
   203  	id := uuid.New().String()
   204  	obj := types.VStorageObject{
   205  		Config: types.VStorageObjectConfigInfo{
   206  			BaseConfigInfo: types.BaseConfigInfo{
   207  				Id: types.ID{
   208  					Id: id,
   209  				},
   210  				Name:                        req.Spec.Name,
   211  				CreateTime:                  time.Now(),
   212  				KeepAfterDeleteVm:           req.Spec.KeepAfterDeleteVm,
   213  				RelocationDisabled:          types.NewBool(false),
   214  				NativeSnapshotSupported:     types.NewBool(false),
   215  				ChangedBlockTrackingEnabled: types.NewBool(false),
   216  				Iofilter:                    nil,
   217  			},
   218  			CapacityInMB:    req.Spec.CapacityInMB,
   219  			ConsumptionType: []string{"disk"},
   220  			ConsumerId:      nil,
   221  		},
   222  	}
   223  
   224  	backing := req.Spec.BackingSpec.(*types.VslmCreateSpecDiskFileBackingSpec)
   225  	path := object.DatastorePath{
   226  		Datastore: ds.Name,
   227  		Path:      backing.Path,
   228  	}
   229  	if path.Path == "" {
   230  		path.Path = dir + "/" + id + ".vmdk"
   231  	}
   232  
   233  	if !register {
   234  		err := vdmCreateVirtualDisk(ctx, types.VirtualDeviceConfigSpecFileOperationCreate, &types.CreateVirtualDisk_Task{
   235  			Datacenter: &dc.Self,
   236  			Name:       path.String(),
   237  			Spec:       &types.FileBackedVirtualDiskSpec{CapacityKb: req.Spec.CapacityInMB * 1024},
   238  		})
   239  		if err != nil {
   240  			return nil, err
   241  		}
   242  	}
   243  
   244  	obj.Config.Backing = &types.BaseConfigInfoDiskFileBackingInfo{
   245  		BaseConfigInfoFileBackingInfo: types.BaseConfigInfoFileBackingInfo{
   246  			BaseConfigInfoBackingInfo: types.BaseConfigInfoBackingInfo{
   247  				Datastore: ds.Self,
   248  			},
   249  			FilePath:        path.String(),
   250  			BackingObjectId: uuid.New().String(),
   251  			Parent:          nil,
   252  			DeltaSizeInMB:   0,
   253  		},
   254  		ProvisioningType: backing.ProvisioningType,
   255  	}
   256  
   257  	objects[obj.Config.Id] = &VStorageObject{VStorageObject: obj}
   258  
   259  	return &obj, nil
   260  
   261  }
   262  
   263  func (m *VcenterVStorageObjectManager) CreateDiskTask(ctx *Context, req *types.CreateDisk_Task) soap.HasFault {
   264  	task := CreateTask(m, "createDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
   265  		return m.createObject(ctx, req, false)
   266  	})
   267  
   268  	return &methods.CreateDisk_TaskBody{
   269  		Res: &types.CreateDisk_TaskResponse{
   270  			Returnval: task.Run(ctx),
   271  		},
   272  	}
   273  }
   274  
   275  func (m *VcenterVStorageObjectManager) DeleteVStorageObjectTask(ctx *Context, req *types.DeleteVStorageObject_Task) soap.HasFault {
   276  	task := CreateTask(m, "deleteDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
   277  		obj := m.object(req.Datastore, req.Id)
   278  		if obj == nil {
   279  			return nil, &types.InvalidArgument{}
   280  		}
   281  
   282  		if len(obj.Config.ConsumerId) != 0 {
   283  			return nil, &types.InvalidState{}
   284  		}
   285  
   286  		backing := obj.Config.Backing.(*types.BaseConfigInfoDiskFileBackingInfo)
   287  		ds := ctx.Map.Get(req.Datastore).(*Datastore)
   288  		dc := ctx.Map.getEntityDatacenter(ds)
   289  		dm := ctx.Map.VirtualDiskManager()
   290  		dm.DeleteVirtualDiskTask(ctx, &types.DeleteVirtualDisk_Task{
   291  			Name:       backing.FilePath,
   292  			Datacenter: &dc.Self,
   293  		})
   294  
   295  		delete(m.objects[req.Datastore], req.Id)
   296  
   297  		return nil, nil
   298  	})
   299  
   300  	return &methods.DeleteVStorageObject_TaskBody{
   301  		Res: &types.DeleteVStorageObject_TaskResponse{
   302  			Returnval: task.Run(ctx),
   303  		},
   304  	}
   305  }
   306  
   307  func (m *VcenterVStorageObjectManager) RetrieveSnapshotInfo(req *types.RetrieveSnapshotInfo) soap.HasFault {
   308  	body := new(methods.RetrieveSnapshotInfoBody)
   309  
   310  	obj := m.object(req.Datastore, req.Id)
   311  	if obj == nil {
   312  		body.Fault_ = Fault("", new(types.InvalidArgument))
   313  	} else {
   314  		body.Res = &types.RetrieveSnapshotInfoResponse{
   315  			Returnval: obj.VStorageObjectSnapshotInfo,
   316  		}
   317  	}
   318  
   319  	return body
   320  }
   321  
   322  func (m *VcenterVStorageObjectManager) VStorageObjectCreateSnapshotTask(ctx *Context, req *types.VStorageObjectCreateSnapshot_Task) soap.HasFault {
   323  	task := CreateTask(m, "createSnapshot", func(*Task) (types.AnyType, types.BaseMethodFault) {
   324  		obj := m.object(req.Datastore, req.Id)
   325  		if obj == nil {
   326  			return nil, new(types.InvalidArgument)
   327  		}
   328  
   329  		snapshot := types.VStorageObjectSnapshotInfoVStorageObjectSnapshot{
   330  			Id: &types.ID{
   331  				Id: uuid.New().String(),
   332  			},
   333  			BackingObjectId: uuid.New().String(),
   334  			CreateTime:      time.Now(),
   335  			Description:     req.Description,
   336  		}
   337  		obj.Snapshots = append(obj.Snapshots, snapshot)
   338  
   339  		return snapshot.Id, nil
   340  	})
   341  
   342  	return &methods.VStorageObjectCreateSnapshot_TaskBody{
   343  		Res: &types.VStorageObjectCreateSnapshot_TaskResponse{
   344  			Returnval: task.Run(ctx),
   345  		},
   346  	}
   347  }
   348  
   349  func (m *VcenterVStorageObjectManager) ExtendDiskTask(ctx *Context, req *types.ExtendDisk_Task) soap.HasFault {
   350  	task := CreateTask(m, "extendDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
   351  		obj := m.object(req.Datastore, req.Id)
   352  		if obj == nil {
   353  			return nil, new(types.InvalidArgument)
   354  		}
   355  
   356  		obj.Config.CapacityInMB = req.NewCapacityInMB
   357  		return nil, nil
   358  	})
   359  	return &methods.ExtendDisk_TaskBody{
   360  		Res: &types.ExtendDisk_TaskResponse{
   361  			Returnval: task.Run(ctx),
   362  		},
   363  	}
   364  }
   365  
   366  func (m *VcenterVStorageObjectManager) DeleteSnapshotTask(ctx *Context, req *types.DeleteSnapshot_Task) soap.HasFault {
   367  	task := CreateTask(m, "deleteSnapshot", func(*Task) (types.AnyType, types.BaseMethodFault) {
   368  		obj := m.object(req.Datastore, req.Id)
   369  		if obj != nil {
   370  			for i := range obj.Snapshots {
   371  				if *obj.Snapshots[i].Id == req.SnapshotId {
   372  					obj.Snapshots = append(obj.Snapshots[:i], obj.Snapshots[i+1:]...)
   373  					return nil, nil
   374  				}
   375  			}
   376  		}
   377  		return nil, new(types.InvalidArgument)
   378  	})
   379  
   380  	return &methods.DeleteSnapshot_TaskBody{
   381  		Res: &types.DeleteSnapshot_TaskResponse{
   382  			Returnval: task.Run(ctx),
   383  		},
   384  	}
   385  }
   386  
   387  func (m *VcenterVStorageObjectManager) tagID(id types.ID) types.ManagedObjectReference {
   388  	return types.ManagedObjectReference{
   389  		Type:  "fcd",
   390  		Value: id.Id,
   391  	}
   392  }
   393  
   394  func (m *VcenterVStorageObjectManager) AttachTagToVStorageObject(ctx *Context, req *types.AttachTagToVStorageObject) soap.HasFault {
   395  	body := new(methods.AttachTagToVStorageObjectBody)
   396  	ref := m.tagID(req.Id)
   397  
   398  	err := ctx.Map.tagManager.AttachTag(ref, types.VslmTagEntry{
   399  		ParentCategoryName: req.Category,
   400  		TagName:            req.Tag,
   401  	})
   402  
   403  	if err == nil {
   404  		body.Res = new(types.AttachTagToVStorageObjectResponse)
   405  	} else {
   406  		body.Fault_ = Fault("", err)
   407  	}
   408  
   409  	return body
   410  }
   411  
   412  func (m *VcenterVStorageObjectManager) DetachTagFromVStorageObject(ctx *Context, req *types.DetachTagFromVStorageObject) soap.HasFault {
   413  	body := new(methods.DetachTagFromVStorageObjectBody)
   414  	ref := m.tagID(req.Id)
   415  
   416  	err := ctx.Map.tagManager.DetachTag(ref, types.VslmTagEntry{
   417  		ParentCategoryName: req.Category,
   418  		TagName:            req.Tag,
   419  	})
   420  
   421  	if err == nil {
   422  		body.Res = new(types.DetachTagFromVStorageObjectResponse)
   423  	} else {
   424  		body.Fault_ = Fault("", err)
   425  	}
   426  
   427  	return body
   428  }
   429  
   430  func (m *VcenterVStorageObjectManager) ListVStorageObjectsAttachedToTag(ctx *Context, req *types.ListVStorageObjectsAttachedToTag) soap.HasFault {
   431  	body := new(methods.ListVStorageObjectsAttachedToTagBody)
   432  
   433  	refs, err := ctx.Map.tagManager.AttachedObjects(types.VslmTagEntry{
   434  		ParentCategoryName: req.Category,
   435  		TagName:            req.Tag,
   436  	})
   437  
   438  	if err == nil {
   439  		body.Res = new(types.ListVStorageObjectsAttachedToTagResponse)
   440  		for _, ref := range refs {
   441  			body.Res.Returnval = append(body.Res.Returnval, types.ID{Id: ref.Value})
   442  		}
   443  	} else {
   444  		body.Fault_ = Fault("", err)
   445  	}
   446  
   447  	return body
   448  }
   449  
   450  func (m *VcenterVStorageObjectManager) ListTagsAttachedToVStorageObject(ctx *Context, req *types.ListTagsAttachedToVStorageObject) soap.HasFault {
   451  	body := new(methods.ListTagsAttachedToVStorageObjectBody)
   452  	ref := m.tagID(req.Id)
   453  
   454  	tags, err := ctx.Map.tagManager.AttachedTags(ref)
   455  
   456  	if err == nil {
   457  		body.Res = &types.ListTagsAttachedToVStorageObjectResponse{
   458  			Returnval: tags,
   459  		}
   460  	} else {
   461  		body.Fault_ = Fault("", err)
   462  	}
   463  
   464  	return body
   465  }
   466  
   467  func (m *VcenterVStorageObjectManager) VCenterUpdateVStorageObjectMetadataExTask(ctx *Context, req *types.VCenterUpdateVStorageObjectMetadataEx_Task) soap.HasFault {
   468  	task := CreateTask(m, "updateVStorageObjectMetadataEx", func(*Task) (types.AnyType, types.BaseMethodFault) {
   469  		obj := m.object(req.Datastore, req.Id)
   470  		if obj == nil {
   471  			return nil, new(types.InvalidArgument)
   472  		}
   473  
   474  		var metadata []types.KeyValue
   475  
   476  		remove := func(key string) bool {
   477  			for _, dk := range req.DeleteKeys {
   478  				if key == dk {
   479  					return true
   480  				}
   481  			}
   482  			return false
   483  		}
   484  
   485  		for _, kv := range obj.Metadata {
   486  			if !remove(kv.Key) {
   487  				metadata = append(metadata, kv)
   488  			}
   489  		}
   490  
   491  		update := func(kv types.KeyValue) bool {
   492  			for i := range obj.Metadata {
   493  				if obj.Metadata[i].Key == kv.Key {
   494  					obj.Metadata[i] = kv
   495  					return true
   496  				}
   497  			}
   498  			return false
   499  		}
   500  
   501  		for _, kv := range req.Metadata {
   502  			if !update(kv) {
   503  				metadata = append(metadata, kv)
   504  			}
   505  		}
   506  
   507  		obj.Metadata = metadata
   508  
   509  		return nil, nil
   510  	})
   511  
   512  	return &methods.VCenterUpdateVStorageObjectMetadataEx_TaskBody{
   513  		Res: &types.VCenterUpdateVStorageObjectMetadataEx_TaskResponse{
   514  			Returnval: task.Run(ctx),
   515  		},
   516  	}
   517  }