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  }