github.com/vmware/govmomi@v0.37.2/simulator/vstorage_object_manager.go (about)

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