github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/disk.go (about) 1 /* 2 * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. 3 */ 4 5 package govcd 6 7 import ( 8 "errors" 9 "fmt" 10 "net/http" 11 "net/url" 12 "strings" 13 14 "github.com/vmware/go-vcloud-director/v2/types/v56" 15 "github.com/vmware/go-vcloud-director/v2/util" 16 ) 17 18 // Independent disk 19 type Disk struct { 20 Disk *types.Disk 21 client *Client 22 } 23 24 // Independent disk query record 25 type DiskRecord struct { 26 Disk *types.DiskRecordType 27 client *Client 28 } 29 30 // Init independent disk struct 31 func NewDisk(cli *Client) *Disk { 32 return &Disk{ 33 Disk: new(types.Disk), 34 client: cli, 35 } 36 } 37 38 // Create instance with reference to types.DiskRecordType 39 func NewDiskRecord(cli *Client) *DiskRecord { 40 return &DiskRecord{ 41 Disk: new(types.DiskRecordType), 42 client: cli, 43 } 44 } 45 46 // Create an independent disk in VDC 47 // Reference: vCloud API Programming Guide for Service Providers vCloud API 30.0 PDF Page 102 - 103, 48 // https://vdc-download.vmware.com/vmwb-repository/dcr-public/1b6cf07d-adb3-4dba-8c47-9c1c92b04857/ 49 // 241956dd-e128-4fcc-8131-bf66e1edd895/vcloud_sp_api_guide_30_0.pdf 50 func (vdc *Vdc) CreateDisk(diskCreateParams *types.DiskCreateParams) (Task, error) { 51 util.Logger.Printf("[TRACE] Create disk, name: %s, size: %d \n", 52 diskCreateParams.Disk.Name, 53 diskCreateParams.Disk.SizeMb, 54 ) 55 56 if diskCreateParams.Disk.Name == "" { 57 return Task{}, fmt.Errorf("disk name is required") 58 } 59 60 var err error 61 var createDiskLink *types.Link 62 63 // Find the proper link for request 64 for _, vdcLink := range vdc.Vdc.Link { 65 if vdcLink.Rel == types.RelAdd && vdcLink.Type == types.MimeDiskCreateParams { 66 util.Logger.Printf("[TRACE] Create disk - found the proper link for request, HREF: %s, name: %s, type: %s, id: %s, rel: %s \n", 67 vdcLink.HREF, 68 vdcLink.Name, 69 vdcLink.Type, 70 vdcLink.ID, 71 vdcLink.Rel) 72 createDiskLink = vdcLink 73 break 74 } 75 } 76 77 if createDiskLink == nil { 78 return Task{}, fmt.Errorf("could not find request URL for create disk in vdc Link") 79 } 80 81 // Prepare the request payload 82 diskCreateParams.Xmlns = types.XMLNamespaceVCloud 83 84 disk := NewDisk(vdc.client) 85 86 _, err = vdc.client.ExecuteRequestWithApiVersion(createDiskLink.HREF, http.MethodPost, 87 createDiskLink.Type, "error create disk: %s", diskCreateParams, disk.Disk, 88 vdc.client.GetSpecificApiVersionOnCondition(">= 36.0", "36.0")) 89 if err != nil { 90 return Task{}, err 91 } 92 // Obtain disk task 93 if disk.Disk.Tasks.Task == nil || len(disk.Disk.Tasks.Task) == 0 { 94 return Task{}, errors.New("error cannot find disk creation task in API response") 95 } 96 task := NewTask(vdc.client) 97 if disk.Disk.Tasks == nil || len(disk.Disk.Tasks.Task) == 0 { 98 return Task{}, fmt.Errorf("no task found after disk %s creation", diskCreateParams.Disk.Name) 99 } 100 task.Task = disk.Disk.Tasks.Task[0] 101 102 util.Logger.Printf("[TRACE] AFTER CREATE DISK\n %s\n", prettyDisk(*disk.Disk)) 103 // Return the disk 104 return *task, nil 105 } 106 107 // Update an independent disk 108 // 1 Verify the independent disk is not connected to any VM 109 // 2 Use newDiskInfo to change update the independent disk 110 // 3 Return task of independent disk update 111 // If the independent disk is connected to a VM, the task will be failed. 112 // Reference: vCloud API Programming Guide for Service Providers vCloud API 30.0 PDF Page 104 - 106, 113 // https://vdc-download.vmware.com/vmwb-repository/dcr-public/1b6cf07d-adb3-4dba-8c47-9c1c92b04857/ 114 // 241956dd-e128-4fcc-8131-bf66e1edd895/vcloud_sp_api_guide_30_0.pdf 115 func (disk *Disk) Update(newDiskInfo *types.Disk) (Task, error) { 116 util.Logger.Printf("[TRACE] Update disk, name: %s, size: %d, HREF: %s \n", 117 newDiskInfo.Name, 118 newDiskInfo.SizeMb, 119 disk.Disk.HREF, 120 ) 121 122 var err error 123 124 if newDiskInfo.Name == "" { 125 return Task{}, fmt.Errorf("disk name is required") 126 } 127 128 // Verify the independent disk is not connected to any VM 129 vmRef, err := disk.AttachedVM() 130 if err != nil { 131 return Task{}, fmt.Errorf("error find attached VM: %s", err) 132 } 133 if vmRef != nil { 134 return Task{}, errors.New("error disk is attached") 135 } 136 137 var updateDiskLink *types.Link 138 139 // Find the proper link for request 140 for _, diskLink := range disk.Disk.Link { 141 if diskLink.Rel == types.RelEdit && diskLink.Type == types.MimeDisk { 142 util.Logger.Printf("[TRACE] Update disk - found the proper link for request, HREF: %s, name: %s, type: %s,id: %s, rel: %s \n", 143 diskLink.HREF, 144 diskLink.Name, 145 diskLink.Type, 146 diskLink.ID, 147 diskLink.Rel) 148 updateDiskLink = diskLink 149 break 150 } 151 } 152 153 if updateDiskLink == nil { 154 return Task{}, fmt.Errorf("could not find request URL for update disk in disk Link") 155 } 156 157 // Prepare the request payload 158 xmlPayload := &types.Disk{ 159 Xmlns: types.XMLNamespaceVCloud, 160 Description: newDiskInfo.Description, 161 SizeMb: newDiskInfo.SizeMb, 162 Name: newDiskInfo.Name, 163 StorageProfile: newDiskInfo.StorageProfile, 164 Owner: newDiskInfo.Owner, 165 } 166 167 // Return the task 168 return disk.client.ExecuteTaskRequestWithApiVersion(updateDiskLink.HREF, http.MethodPut, 169 updateDiskLink.Type, "error updating disk: %s", xmlPayload, 170 disk.client.GetSpecificApiVersionOnCondition(">= 36.0", "36.0")) 171 } 172 173 // Remove an independent disk 174 // 1 Verify the independent disk is not connected to any VM 175 // 2 Delete the independent disk. Make a DELETE request to the URL in the rel="remove" link in the Disk 176 // 3 Return task of independent disk deletion 177 // If the independent disk is connected to a VM, the task will be failed. 178 // Reference: vCloud API Programming Guide for Service Providers vCloud API 30.0 PDF Page 106 - 107, 179 // https://vdc-download.vmware.com/vmwb-repository/dcr-public/1b6cf07d-adb3-4dba-8c47-9c1c92b04857/ 180 // 241956dd-e128-4fcc-8131-bf66e1edd895/vcloud_sp_api_guide_30_0.pdf 181 func (disk *Disk) Delete() (Task, error) { 182 util.Logger.Printf("[TRACE] Delete disk, HREF: %s \n", disk.Disk.HREF) 183 184 var err error 185 186 // Verify the independent disk is not connected to any VM 187 vmRef, err := disk.AttachedVM() 188 if err != nil { 189 return Task{}, fmt.Errorf("error find attached VM: %s", err) 190 } 191 if vmRef != nil { 192 return Task{}, errors.New("error disk is attached") 193 } 194 195 var deleteDiskLink *types.Link 196 197 // Find the proper link for request 198 for _, diskLink := range disk.Disk.Link { 199 if diskLink.Rel == types.RelRemove { 200 util.Logger.Printf("[TRACE] Delete disk - found the proper link for request, HREF: %s, name: %s, type: %s,id: %s, rel: %s \n", 201 diskLink.HREF, 202 diskLink.Name, 203 diskLink.Type, 204 diskLink.ID, 205 diskLink.Rel) 206 deleteDiskLink = diskLink 207 break 208 } 209 } 210 211 if deleteDiskLink == nil { 212 return Task{}, fmt.Errorf("could not find request URL for delete disk in disk Link") 213 } 214 215 // Return the task 216 return disk.client.ExecuteTaskRequest(deleteDiskLink.HREF, http.MethodDelete, 217 "", "error delete disk: %s", nil) 218 } 219 220 // Refresh the disk information by disk href 221 func (disk *Disk) Refresh() error { 222 if disk.Disk == nil || disk.Disk.HREF == "" { 223 return fmt.Errorf("cannot refresh, Object is empty") 224 } 225 util.Logger.Printf("[TRACE] Disk refresh, HREF: %s\n", disk.Disk.HREF) 226 227 unmarshalledDisk := &types.Disk{} 228 229 _, err := disk.client.ExecuteRequestWithApiVersion(disk.Disk.HREF, http.MethodGet, 230 "", "error refreshing independent disk: %s", nil, unmarshalledDisk, 231 disk.client.GetSpecificApiVersionOnCondition(">= 36.0", "36.0")) 232 if err != nil { 233 return err 234 } 235 disk.Disk = unmarshalledDisk 236 237 // The request was successful 238 return nil 239 } 240 241 // Get a VM that is attached the disk 242 // An independent disk can be attached to at most one virtual machine. 243 // If the disk isn't attached to any VM, return empty VM reference and no error. 244 // Otherwise return the first VM reference and no error. 245 // Reference: vCloud API Programming Guide for Service Providers vCloud API 30.0 PDF Page 107, 246 // https://vdc-download.vmware.com/vmwb-repository/dcr-public/1b6cf07d-adb3-4dba-8c47-9c1c92b04857/ 247 // 241956dd-e128-4fcc-8131-bf66e1edd895/vcloud_sp_api_guide_30_0.pdf 248 func (disk *Disk) AttachedVM() (*types.Reference, error) { 249 util.Logger.Printf("[TRACE] Disk attached VM, HREF: %s\n", disk.Disk.HREF) 250 251 var attachedVMLink *types.Link 252 253 // Find the proper link for request 254 for _, diskLink := range disk.Disk.Link { 255 if diskLink.Type == types.MimeVMs { 256 util.Logger.Printf("[TRACE] Disk attached VM - found the proper link for request, HREF: %s, name: %s, type: %s,id: %s, rel: %s \n", 257 diskLink.HREF, 258 diskLink.Name, 259 diskLink.Type, 260 diskLink.ID, 261 diskLink.Rel) 262 263 attachedVMLink = diskLink 264 break 265 } 266 } 267 268 if attachedVMLink == nil { 269 return nil, fmt.Errorf("could not find request URL for attached vm in disk Link") 270 } 271 272 // Decode request 273 var vms = new(types.Vms) 274 275 _, err := disk.client.ExecuteRequest(attachedVMLink.HREF, http.MethodGet, 276 attachedVMLink.Type, "error getting attached vms: %s", nil, vms) 277 if err != nil { 278 return nil, err 279 } 280 281 // If disk is not attached to any VM 282 if vms.VmReference == nil || len(vms.VmReference) == 0 { 283 return nil, nil 284 } 285 286 // An independent disk can be attached to at most one virtual machine so return the first result of VM reference 287 return vms.VmReference[0], nil 288 } 289 290 // Find an independent disk by disk href in VDC 291 // Deprecated: Use VDC.GetDiskByHref() 292 func (vdc *Vdc) FindDiskByHREF(href string) (*Disk, error) { 293 util.Logger.Printf("[TRACE] VDC find disk By HREF: %s\n", href) 294 295 return FindDiskByHREF(vdc.client, href) 296 } 297 298 // Find an independent disk by VDC client and disk href 299 // Deprecated: Use VDC.GetDiskByHref() 300 func FindDiskByHREF(client *Client, href string) (*Disk, error) { 301 util.Logger.Printf("[TRACE] Find disk By HREF: %s\n", href) 302 303 disk := NewDisk(client) 304 305 _, err := client.ExecuteRequest(href, http.MethodGet, 306 "", "error finding disk: %s", nil, disk.Disk) 307 308 // Return the disk 309 return disk, err 310 311 } 312 313 // QueryDisk find independent disk using disk name. Returns DiskRecord type 314 func (vdc *Vdc) QueryDisk(diskName string) (DiskRecord, error) { 315 316 if diskName == "" { 317 return DiskRecord{}, fmt.Errorf("disk name can not be empty") 318 } 319 320 typeMedia := "disk" 321 if vdc.client.IsSysAdmin { 322 typeMedia = "adminDisk" 323 } 324 325 results, err := vdc.QueryWithNotEncodedParamsWithApiVersion(nil, map[string]string{"type": typeMedia, 326 "filter": "name==" + url.QueryEscape(diskName) + ";vdc==" + vdc.vdcId(), "filterEncoded": "true"}, 327 vdc.client.GetSpecificApiVersionOnCondition(">= 36.0", "36.0")) 328 if err != nil { 329 return DiskRecord{}, fmt.Errorf("error querying disk %s", err) 330 } 331 332 diskResults := results.Results.DiskRecord 333 if vdc.client.IsSysAdmin { 334 diskResults = results.Results.AdminDiskRecord 335 } 336 337 newDisk := NewDiskRecord(vdc.client) 338 339 if len(diskResults) == 1 { 340 newDisk.Disk = diskResults[0] 341 } else { 342 return DiskRecord{}, fmt.Errorf("found results %d", len(diskResults)) 343 } 344 345 return *newDisk, nil 346 } 347 348 // QueryDisks find independent disks using disk name. Returns list of DiskRecordType 349 func (vdc *Vdc) QueryDisks(diskName string) (*[]*types.DiskRecordType, error) { 350 351 if diskName == "" { 352 return nil, fmt.Errorf("disk name can't be empty") 353 } 354 355 typeMedia := "disk" 356 if vdc.client.IsSysAdmin { 357 typeMedia = "adminDisk" 358 } 359 360 results, err := vdc.QueryWithNotEncodedParamsWithApiVersion(nil, map[string]string{"type": typeMedia, 361 "filter": "name==" + url.QueryEscape(diskName) + ";vdc==" + vdc.vdcId(), "filterEncoded": "true"}, 362 vdc.client.GetSpecificApiVersionOnCondition(">= 36.0", "36.0")) 363 if err != nil { 364 return nil, fmt.Errorf("error querying disks %s", err) 365 } 366 367 diskResults := results.Results.DiskRecord 368 if vdc.client.IsSysAdmin { 369 diskResults = results.Results.AdminDiskRecord 370 } 371 372 return &diskResults, nil 373 } 374 375 // GetDiskByHref finds a Disk by HREF 376 // On success, returns a pointer to the Disk structure and a nil error 377 // On failure, returns a nil pointer and an error 378 func (vdc *Vdc) GetDiskByHref(diskHref string) (*Disk, error) { 379 util.Logger.Printf("[TRACE] Get Disk By Href: %s\n", diskHref) 380 Disk := NewDisk(vdc.client) 381 382 _, err := vdc.client.ExecuteRequestWithApiVersion(diskHref, http.MethodGet, 383 "", "error retrieving Disk: %s", nil, Disk.Disk, 384 vdc.client.GetSpecificApiVersionOnCondition(">= 36.0", "36.0")) 385 if err != nil && (strings.Contains(err.Error(), "MajorErrorCode:403") || strings.Contains(err.Error(), "does not exist")) { 386 return nil, ErrorEntityNotFound 387 } 388 if err != nil { 389 return nil, err 390 } 391 return Disk, nil 392 } 393 394 // GetDisksByName finds one or more Disks by Name 395 // On success, returns a pointer to the Disk list and a nil error 396 // On failure, returns a nil pointer and an error 397 func (vdc *Vdc) GetDisksByName(diskName string, refresh bool) (*[]Disk, error) { 398 util.Logger.Printf("[TRACE] Get Disk By Name: %s\n", diskName) 399 var diskList []Disk 400 if refresh { 401 err := vdc.Refresh() 402 if err != nil { 403 return nil, err 404 } 405 } 406 for _, resourceEntities := range vdc.Vdc.ResourceEntities { 407 for _, resourceEntity := range resourceEntities.ResourceEntity { 408 if resourceEntity.Name == diskName && resourceEntity.Type == "application/vnd.vmware.vcloud.disk+xml" { 409 disk, err := vdc.GetDiskByHref(resourceEntity.HREF) 410 if err != nil { 411 return nil, err 412 } 413 diskList = append(diskList, *disk) 414 } 415 } 416 } 417 if len(diskList) == 0 { 418 return nil, ErrorEntityNotFound 419 } 420 return &diskList, nil 421 } 422 423 // GetDiskById finds a Disk by ID 424 // On success, returns a pointer to the Disk structure and a nil error 425 // On failure, returns a nil pointer and an error 426 func (vdc *Vdc) GetDiskById(diskId string, refresh bool) (*Disk, error) { 427 util.Logger.Printf("[TRACE] Get Disk By Id: %s\n", diskId) 428 if refresh { 429 err := vdc.Refresh() 430 if err != nil { 431 return nil, err 432 } 433 } 434 for _, resourceEntities := range vdc.Vdc.ResourceEntities { 435 for _, resourceEntity := range resourceEntities.ResourceEntity { 436 if equalIds(diskId, resourceEntity.ID, resourceEntity.HREF) && resourceEntity.Type == "application/vnd.vmware.vcloud.disk+xml" { 437 return vdc.GetDiskByHref(resourceEntity.HREF) 438 } 439 } 440 } 441 return nil, ErrorEntityNotFound 442 } 443 444 // Get a VMs HREFs that is attached to the disk 445 // An independent disk can be attached to at most one virtual machine. 446 // If the disk isn't attached to any VM, return empty slice. 447 // Otherwise return the list of VMs HREFs. 448 func (disk *Disk) GetAttachedVmsHrefs() ([]string, error) { 449 util.Logger.Printf("[TRACE] GetAttachedVmsHrefs, HREF: %s\n", disk.Disk.HREF) 450 451 var vmHrefs []string 452 453 var attachedVMsLink *types.Link 454 455 // Find the proper link for request 456 for _, diskLink := range disk.Disk.Link { 457 if diskLink.Type == types.MimeVMs { 458 util.Logger.Printf("[TRACE] GetAttachedVmsHrefs - found the proper link for request, HREF: %s, name: %s, type: %s,id: %s, rel: %s \n", 459 diskLink.HREF, diskLink.Name, diskLink.Type, diskLink.ID, diskLink.Rel) 460 461 attachedVMsLink = diskLink 462 break 463 } 464 } 465 466 if attachedVMsLink == nil { 467 return nil, fmt.Errorf("error GetAttachedVmsHrefs - could not find request URL for attached vm in disk Link") 468 } 469 470 // Decode request 471 var vms = new(types.Vms) 472 473 _, err := disk.client.ExecuteRequest(attachedVMsLink.HREF, http.MethodGet, 474 attachedVMsLink.Type, "error GetAttachedVmsHrefs - error getting attached VMs: %s", nil, vms) 475 if err != nil { 476 return nil, err 477 } 478 479 // If disk is not attached to any VM 480 if vms.VmReference == nil || len(vms.VmReference) == 0 { 481 return nil, nil 482 } 483 484 for _, value := range vms.VmReference { 485 vmHrefs = append(vmHrefs, value.HREF) 486 } 487 488 return vmHrefs, nil 489 }