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 }