github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/provider_vdc.go (about)

     1  package govcd
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http"
     7  	"net/url"
     8  	"time"
     9  
    10  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    11  	"github.com/vmware/go-vcloud-director/v2/util"
    12  )
    13  
    14  // ProviderVdc is the basic Provider VDC structure, contains the minimum set of attributes.
    15  type ProviderVdc struct {
    16  	ProviderVdc *types.ProviderVdc
    17  	client      *Client
    18  }
    19  
    20  // ProviderVdcExtended is the extended Provider VDC structure, contains same attributes as ProviderVdc plus some more.
    21  type ProviderVdcExtended struct {
    22  	VMWProviderVdc *types.VMWProviderVdc
    23  	client         *Client
    24  }
    25  
    26  func newProviderVdc(cli *Client) *ProviderVdc {
    27  	return &ProviderVdc{
    28  		ProviderVdc: new(types.ProviderVdc),
    29  		client:      cli,
    30  	}
    31  }
    32  
    33  func newProviderVdcExtended(cli *Client) *ProviderVdcExtended {
    34  	return &ProviderVdcExtended{
    35  		VMWProviderVdc: new(types.VMWProviderVdc),
    36  		client:         cli,
    37  	}
    38  }
    39  
    40  // GetProviderVdcByHref finds a Provider VDC by its HREF.
    41  // On success, returns a pointer to the ProviderVdc structure and a nil error
    42  // On failure, returns a nil pointer and an error
    43  func (vcdClient *VCDClient) GetProviderVdcByHref(providerVdcHref string) (*ProviderVdc, error) {
    44  	providerVdc := newProviderVdc(&vcdClient.Client)
    45  
    46  	_, err := vcdClient.Client.ExecuteRequest(providerVdcHref, http.MethodGet,
    47  		"", "error retrieving Provider VDC: %s", nil, providerVdc.ProviderVdc)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	return providerVdc, nil
    53  }
    54  
    55  // GetProviderVdcExtendedByHref finds a Provider VDC with extended attributes by its HREF.
    56  // On success, returns a pointer to the ProviderVdcExtended structure and a nil error
    57  // On failure, returns a nil pointer and an error
    58  func (vcdClient *VCDClient) GetProviderVdcExtendedByHref(providerVdcHref string) (*ProviderVdcExtended, error) {
    59  	providerVdc := newProviderVdcExtended(&vcdClient.Client)
    60  
    61  	_, err := vcdClient.Client.ExecuteRequest(getAdminExtensionURL(providerVdcHref), http.MethodGet,
    62  		"", "error retrieving extended Provider VDC: %s", nil, providerVdc.VMWProviderVdc)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	return providerVdc, nil
    68  }
    69  
    70  // GetProviderVdcById finds a Provider VDC by URN.
    71  // On success, returns a pointer to the ProviderVdc structure and a nil error
    72  // On failure, returns a nil pointer and an error
    73  func (vcdClient *VCDClient) GetProviderVdcById(providerVdcId string) (*ProviderVdc, error) {
    74  	providerVdcHref := vcdClient.Client.VCDHREF
    75  	providerVdcHref.Path += "/admin/providervdc/" + extractUuid(providerVdcId)
    76  
    77  	return vcdClient.GetProviderVdcByHref(providerVdcHref.String())
    78  }
    79  
    80  // GetProviderVdcExtendedById finds a Provider VDC with extended attributes by URN.
    81  // On success, returns a pointer to the ProviderVdcExtended structure and a nil error
    82  // On failure, returns a nil pointer and an error
    83  func (vcdClient *VCDClient) GetProviderVdcExtendedById(providerVdcId string) (*ProviderVdcExtended, error) {
    84  	providerVdcHref := vcdClient.Client.VCDHREF
    85  	providerVdcHref.Path += "/admin/extension/providervdc/" + extractUuid(providerVdcId)
    86  
    87  	return vcdClient.GetProviderVdcExtendedByHref(providerVdcHref.String())
    88  }
    89  
    90  // GetProviderVdcByName finds a Provider VDC by name.
    91  // On success, returns a pointer to the ProviderVdc structure and a nil error
    92  // On failure, returns a nil pointer and an error
    93  func (vcdClient *VCDClient) GetProviderVdcByName(providerVdcName string) (*ProviderVdc, error) {
    94  	providerVdc, err := getProviderVdcByName(vcdClient, providerVdcName, false)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	return providerVdc.(*ProviderVdc), err
    99  }
   100  
   101  // GetProviderVdcExtendedByName finds a Provider VDC with extended attributes by name.
   102  // On success, returns a pointer to the ProviderVdcExtended structure and a nil error
   103  // On failure, returns a nil pointer and an error
   104  func (vcdClient *VCDClient) GetProviderVdcExtendedByName(providerVdcName string) (*ProviderVdcExtended, error) {
   105  	providerVdcExtended, err := getProviderVdcByName(vcdClient, providerVdcName, true)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	return providerVdcExtended.(*ProviderVdcExtended), err
   110  }
   111  
   112  // Refresh updates the contents of the Provider VDC associated to the receiver object.
   113  func (providerVdc *ProviderVdc) Refresh() error {
   114  	if providerVdc.ProviderVdc.HREF == "" {
   115  		return fmt.Errorf("cannot refresh, receiver Provider VDC is empty")
   116  	}
   117  
   118  	unmarshalledVdc := &types.ProviderVdc{}
   119  
   120  	_, err := providerVdc.client.ExecuteRequest(providerVdc.ProviderVdc.HREF, http.MethodGet,
   121  		"", "error refreshing Provider VDC: %s", nil, unmarshalledVdc)
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	providerVdc.ProviderVdc = unmarshalledVdc
   127  
   128  	return nil
   129  }
   130  
   131  // Refresh updates the contents of the extended Provider VDC associated to the receiver object.
   132  func (providerVdcExtended *ProviderVdcExtended) Refresh() error {
   133  	if providerVdcExtended.VMWProviderVdc.HREF == "" {
   134  		return fmt.Errorf("cannot refresh, receiver extended Provider VDC is empty")
   135  	}
   136  
   137  	unmarshalledVdc := &types.VMWProviderVdc{}
   138  
   139  	_, err := providerVdcExtended.client.ExecuteRequest(providerVdcExtended.VMWProviderVdc.HREF, http.MethodGet,
   140  		"", "error refreshing extended Provider VDC: %s", nil, unmarshalledVdc)
   141  	if err != nil {
   142  		return err
   143  	}
   144  
   145  	providerVdcExtended.VMWProviderVdc = unmarshalledVdc
   146  
   147  	return nil
   148  }
   149  
   150  // ToProviderVdc converts the receiver ProviderVdcExtended into the subset ProviderVdc
   151  func (providerVdcExtended *ProviderVdcExtended) ToProviderVdc() (*ProviderVdc, error) {
   152  	providerVdcHref := providerVdcExtended.client.VCDHREF
   153  	providerVdcHref.Path += "/admin/providervdc/" + extractUuid(providerVdcExtended.VMWProviderVdc.ID)
   154  
   155  	providerVdc := newProviderVdc(providerVdcExtended.client)
   156  
   157  	_, err := providerVdcExtended.client.ExecuteRequest(providerVdcHref.String(), http.MethodGet,
   158  		"", "error retrieving Provider VDC: %s", nil, providerVdc.ProviderVdc)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	return providerVdc, nil
   164  }
   165  
   166  // getProviderVdcByName finds a Provider VDC with extension (extended=true) or without extension (extended=false) by name
   167  // On success, returns a pointer to the ProviderVdc (extended=false) or ProviderVdcExtended (extended=true) structure and a nil error
   168  // On failure, returns a nil pointer and an error
   169  func getProviderVdcByName(vcdClient *VCDClient, providerVdcName string, extended bool) (interface{}, error) {
   170  	foundProviderVdcs, err := vcdClient.QueryWithNotEncodedParams(nil, map[string]string{
   171  		"type":          "providerVdc",
   172  		"filter":        fmt.Sprintf("name==%s", url.QueryEscape(providerVdcName)),
   173  		"filterEncoded": "true",
   174  	})
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	if len(foundProviderVdcs.Results.VMWProviderVdcRecord) == 0 {
   179  		return nil, ErrorEntityNotFound
   180  	}
   181  	if len(foundProviderVdcs.Results.VMWProviderVdcRecord) > 1 {
   182  		return nil, fmt.Errorf("more than one Provider VDC found with name '%s'", providerVdcName)
   183  	}
   184  	if extended {
   185  		return vcdClient.GetProviderVdcExtendedByHref(foundProviderVdcs.Results.VMWProviderVdcRecord[0].HREF)
   186  	}
   187  	return vcdClient.GetProviderVdcByHref(foundProviderVdcs.Results.VMWProviderVdcRecord[0].HREF)
   188  }
   189  
   190  // CreateProviderVdc creates a new provider VDC using the passed parameters
   191  func (vcdClient *VCDClient) CreateProviderVdc(params *types.ProviderVdcCreation) (*ProviderVdcExtended, error) {
   192  	if !vcdClient.Client.IsSysAdmin {
   193  		return nil, fmt.Errorf("functionality requires System Administrator privileges")
   194  	}
   195  	if params.Name == "" {
   196  		return nil, fmt.Errorf("a non-empty name is needed to create a provider VDC")
   197  	}
   198  	if params.ResourcePoolRefs == nil || len(params.ResourcePoolRefs.VimObjectRef) == 0 {
   199  		return nil, fmt.Errorf("resource pool is needed to create a provider VDC")
   200  	}
   201  	if len(params.StorageProfile) == 0 {
   202  		return nil, fmt.Errorf("storage profile is needed to create a provider VDC")
   203  	}
   204  	if params.VimServer == nil {
   205  		return nil, fmt.Errorf("vim server is needed to create a provider VDC")
   206  	}
   207  	pvdcCreateHREF := vcdClient.Client.VCDHREF
   208  	pvdcCreateHREF.Path += "/admin/extension/providervdcsparams"
   209  
   210  	resp, err := vcdClient.Client.executeJsonRequest(pvdcCreateHREF.String(), http.MethodPost, params, "error creating provider VDC: %s")
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  
   215  	body, _ := io.ReadAll(resp.Body)
   216  	util.ProcessResponseOutput(util.CallFuncName(), resp, string(body))
   217  
   218  	defer closeBody(resp)
   219  
   220  	pvdc, err := vcdClient.GetProviderVdcExtendedByName(params.Name)
   221  	if err != nil {
   222  		return nil, err
   223  	}
   224  
   225  	// At this stage, the provider VDC is created, but the task may be still working.
   226  	// Thus, we retrieve the associated tasks, and wait for their completion.
   227  	if pvdc.VMWProviderVdc.Tasks == nil {
   228  		err = pvdc.Refresh()
   229  		if err != nil {
   230  			return pvdc, fmt.Errorf("error refreshing provider VDC %s: %s", params.Name, err)
   231  		}
   232  		if pvdc.VMWProviderVdc.Tasks == nil {
   233  			return pvdc, fmt.Errorf("provider VDC %s was created, but no completion task was found: %s", params.Name, err)
   234  		}
   235  	}
   236  	for _, taskInProgress := range pvdc.VMWProviderVdc.Tasks.Task {
   237  		task := Task{
   238  			Task:   taskInProgress,
   239  			client: pvdc.client,
   240  		}
   241  		err = task.WaitTaskCompletion()
   242  		if err != nil {
   243  			return pvdc, fmt.Errorf("provider VDC %s was created, but it is not ready: %s", params.Name, err)
   244  		}
   245  	}
   246  
   247  	err = pvdc.Refresh()
   248  	return pvdc, err
   249  }
   250  
   251  // Disable changes the Provider VDC state from enabled to disabled
   252  func (pvdc *ProviderVdcExtended) Disable() error {
   253  	util.Logger.Printf("[TRACE] ProviderVdc.Disable")
   254  
   255  	href, err := url.JoinPath(pvdc.VMWProviderVdc.HREF, "action", "disable")
   256  
   257  	if err != nil {
   258  		return err
   259  	}
   260  
   261  	err = pvdc.client.ExecuteRequestWithoutResponse(href, http.MethodPost, "", "error disabling provider VDC: %s", nil)
   262  	if err != nil {
   263  		return err
   264  	}
   265  	err = pvdc.Refresh()
   266  	if err != nil {
   267  		return err
   268  	}
   269  	if pvdc.IsEnabled() {
   270  		return fmt.Errorf("provider VDC was disabled, but its status is still shown as 'enabled'")
   271  	}
   272  	return nil
   273  }
   274  
   275  // IsEnabled shows whether the Provider VDC is enabled
   276  func (pvdc *ProviderVdcExtended) IsEnabled() bool {
   277  	if pvdc.VMWProviderVdc.IsEnabled == nil {
   278  		return false
   279  	}
   280  	return *pvdc.VMWProviderVdc.IsEnabled
   281  }
   282  
   283  // Enable changes the Provider VDC state from disabled to enabled
   284  func (pvdc *ProviderVdcExtended) Enable() error {
   285  	util.Logger.Printf("[TRACE] ProviderVdc.Enable")
   286  
   287  	href, err := url.JoinPath(pvdc.VMWProviderVdc.HREF, "action", "enable")
   288  
   289  	if err != nil {
   290  		return err
   291  	}
   292  
   293  	err = pvdc.client.ExecuteRequestWithoutResponse(href, http.MethodPost, "",
   294  		"error enabling provider VDC: %s", nil)
   295  	if err != nil {
   296  		return err
   297  	}
   298  	err = pvdc.Refresh()
   299  	if err != nil {
   300  		return err
   301  	}
   302  	if !pvdc.IsEnabled() {
   303  		return fmt.Errorf("provider VDC was enabled, but its status is still shown as 'disabled'")
   304  	}
   305  	return nil
   306  }
   307  
   308  // Delete removes a Provider VDC
   309  // The provider VDC must be disabled for deletion to succeed
   310  // Deletion will also fail if the Provider VDC is backing other resources, such as organization VDCs
   311  func (pvdc *ProviderVdcExtended) Delete() (Task, error) {
   312  	util.Logger.Printf("[TRACE] ProviderVdc.Delete")
   313  
   314  	if pvdc.IsEnabled() {
   315  		return Task{}, fmt.Errorf("provider VDC %s is enabled - can't delete", pvdc.VMWProviderVdc.Name)
   316  	}
   317  	// Return the task
   318  	return pvdc.client.ExecuteTaskRequest(pvdc.VMWProviderVdc.HREF, http.MethodDelete,
   319  		"", "error deleting provider VDC: %s", nil)
   320  }
   321  
   322  // Update can change some of the provider VDC internals
   323  // In practical terms, only name and description are guaranteed to be changed through this method.
   324  // The other admitted changes need to go through separate API calls
   325  func (pvdc *ProviderVdcExtended) Update() error {
   326  
   327  	resp, err := pvdc.client.executeJsonRequest(pvdc.VMWProviderVdc.HREF, http.MethodPut, pvdc.VMWProviderVdc,
   328  		"error updating provider VDC: %s")
   329  
   330  	if err != nil {
   331  		return err
   332  	}
   333  	defer closeBody(resp)
   334  
   335  	return pvdc.checkProgress("updating")
   336  }
   337  
   338  // Rename changes name and/or description from a provider VDC
   339  func (pvdc *ProviderVdcExtended) Rename(name, description string) error {
   340  	if name == "" {
   341  		return fmt.Errorf("provider VDC name cannot be empty")
   342  	}
   343  	pvdc.VMWProviderVdc.Name = name
   344  	pvdc.VMWProviderVdc.Description = description
   345  	return pvdc.Update()
   346  }
   347  
   348  // AddResourcePools adds resource pools to the Provider VDC
   349  func (pvdc *ProviderVdcExtended) AddResourcePools(resourcePools []*ResourcePool) error {
   350  	util.Logger.Printf("[TRACE] ProviderVdc.AddResourcePools")
   351  
   352  	href, err := url.JoinPath(pvdc.VMWProviderVdc.HREF, "action", "updateResourcePools")
   353  	if err != nil {
   354  		return err
   355  	}
   356  
   357  	var items []*types.VimObjectRef
   358  
   359  	for _, rp := range resourcePools {
   360  		vcenterUrl, err := rp.vcenter.GetVimServerUrl()
   361  		if err != nil {
   362  			return err
   363  		}
   364  		item := types.VimObjectRef{
   365  			MoRef:         rp.ResourcePool.Moref,
   366  			VimObjectType: "RESOURCE_POOL",
   367  			VimServerRef: &types.Reference{
   368  				HREF: vcenterUrl,
   369  				ID:   extractUuid(rp.vcenter.VSphereVCenter.VcId),
   370  				Name: rp.vcenter.VSphereVCenter.Name,
   371  				Type: "application/vnd.vmware.admin.vmwvirtualcenter+xml",
   372  			},
   373  		}
   374  		items = append(items, &item)
   375  	}
   376  
   377  	input := types.AddResourcePool{VimObjectRef: items}
   378  
   379  	resp, err := pvdc.client.executeJsonRequest(href, http.MethodPost, input, "error updating provider VDC resource pools: %s")
   380  	if err != nil {
   381  		return err
   382  	}
   383  	task := NewTask(pvdc.client)
   384  	err = decodeBody(types.BodyTypeJSON, resp, task.Task)
   385  	if err != nil {
   386  		return err
   387  	}
   388  
   389  	defer closeBody(resp)
   390  	err = task.WaitTaskCompletion()
   391  	if err != nil {
   392  		return err
   393  	}
   394  	return pvdc.Refresh()
   395  }
   396  
   397  // DeleteResourcePools removes resource pools from the Provider VDC
   398  func (pvdc *ProviderVdcExtended) DeleteResourcePools(resourcePools []*ResourcePool) error {
   399  	util.Logger.Printf("[TRACE] ProviderVdc.DeleteResourcePools")
   400  
   401  	href, err := url.JoinPath(pvdc.VMWProviderVdc.HREF, "action", "updateResourcePools")
   402  	if err != nil {
   403  		return err
   404  	}
   405  
   406  	usedResourcePools, err := pvdc.GetResourcePools()
   407  	if err != nil {
   408  		return fmt.Errorf("error retrieving used resource pools: %s", err)
   409  	}
   410  
   411  	var items []*types.Reference
   412  
   413  	for _, rp := range resourcePools {
   414  
   415  		var foundUsed *types.QueryResultResourcePoolRecordType
   416  		for _, urp := range usedResourcePools {
   417  			if rp.ResourcePool.Moref == urp.Moref {
   418  				foundUsed = urp
   419  				break
   420  			}
   421  		}
   422  		if foundUsed == nil {
   423  			return fmt.Errorf("resource pool %s not found in provider VDC %s", rp.ResourcePool.Name, pvdc.VMWProviderVdc.Name)
   424  		}
   425  		if foundUsed.IsPrimary {
   426  			return fmt.Errorf("resource pool %s (%s) can not be removed, because it is the primary one for provider VDC %s",
   427  				rp.ResourcePool.Name, rp.ResourcePool.Moref, pvdc.VMWProviderVdc.Name)
   428  		}
   429  		if foundUsed.IsEnabled {
   430  			err = disableResourcePool(pvdc.client, foundUsed.HREF)
   431  			if err != nil {
   432  				return fmt.Errorf("error disabling resource pool %s: %s", foundUsed.Name, err)
   433  			}
   434  		}
   435  
   436  		item := types.Reference{
   437  			HREF: foundUsed.HREF,
   438  			ID:   extractUuid(foundUsed.HREF),
   439  			Name: foundUsed.Name,
   440  			Type: "application/vnd.vmware.admin.vmwProviderVdcResourcePool+xml",
   441  		}
   442  		items = append(items, &item)
   443  	}
   444  
   445  	input := types.DeleteResourcePool{ResourcePoolRefs: items}
   446  
   447  	resp, err := pvdc.client.executeJsonRequest(href, http.MethodPost, input, "error removing resource pools from provider VDC: %s")
   448  	if err != nil {
   449  		return err
   450  	}
   451  	defer closeBody(resp)
   452  	task := NewTask(pvdc.client)
   453  	err = decodeBody(types.BodyTypeJSON, resp, task.Task)
   454  	if err != nil {
   455  		return err
   456  	}
   457  	err = task.WaitTaskCompletion()
   458  	if err != nil {
   459  		return err
   460  	}
   461  	return pvdc.Refresh()
   462  }
   463  
   464  // GetResourcePools returns the Resource Pools belonging to this provider VDC
   465  func (pvdc *ProviderVdcExtended) GetResourcePools() ([]*types.QueryResultResourcePoolRecordType, error) {
   466  	resourcePools, err := pvdc.client.cumulativeQuery(types.QtResourcePool, nil, map[string]string{
   467  		"type":          types.QtResourcePool,
   468  		"filter":        fmt.Sprintf("providerVdc==%s", url.QueryEscape(extractUuid(pvdc.VMWProviderVdc.HREF))),
   469  		"filterEncoded": "true",
   470  	})
   471  	if err != nil {
   472  		return nil, fmt.Errorf("could not get the Resource pool: %s", err)
   473  	}
   474  	return resourcePools.Results.ResourcePoolRecord, nil
   475  }
   476  
   477  // disableResourcePool disables a resource pool while it is assigned to a provider VDC
   478  // Calling this function is a prerequisite to removing a resource pool from a provider VDC
   479  func disableResourcePool(client *Client, resourcePoolHref string) error {
   480  	href, err := url.JoinPath(resourcePoolHref, "action", "disable")
   481  	if err != nil {
   482  		return err
   483  	}
   484  	return client.ExecuteRequestWithoutResponse(href, http.MethodPost, "", "error disabling resource pool: %s", nil)
   485  }
   486  
   487  // AddStorageProfiles adds the given storage profiles in this provider VDC
   488  func (pvdc *ProviderVdcExtended) AddStorageProfiles(storageProfileNames []string) error {
   489  	href, err := url.JoinPath(pvdc.VMWProviderVdc.HREF, "storageProfiles")
   490  	if err != nil {
   491  		return err
   492  	}
   493  
   494  	addStorageProfiles := &types.AddStorageProfiles{AddStorageProfile: storageProfileNames}
   495  
   496  	resp, err := pvdc.client.executeJsonRequest(href, http.MethodPost, addStorageProfiles,
   497  		"error adding storage profiles to provider VDC: %s")
   498  	if err != nil {
   499  		return err
   500  	}
   501  
   502  	defer closeBody(resp)
   503  
   504  	return pvdc.checkProgress("adding storage profiles")
   505  }
   506  
   507  func (pvdc *ProviderVdcExtended) checkProgress(label string) error {
   508  	// Let's keep this timeout as a precaution against an infinite wait
   509  	timeout := 2 * time.Minute
   510  	start := time.Now()
   511  	err := pvdc.Refresh()
   512  	if err != nil {
   513  		return err
   514  	}
   515  
   516  	var elapsed time.Duration
   517  	for ResourceInProgress(pvdc.VMWProviderVdc.Tasks) {
   518  		err = pvdc.Refresh()
   519  		if err != nil {
   520  			return fmt.Errorf("error %s: %s", label, err)
   521  		}
   522  		time.Sleep(200 * time.Millisecond)
   523  		elapsed = time.Since(start)
   524  		if elapsed > timeout {
   525  			return fmt.Errorf("error %s within %s", label, timeout)
   526  		}
   527  	}
   528  	util.Logger.Printf("[ProviderVdcExtended.checkProgress] called by %s - running %s - elapsed: %s\n",
   529  		util.CallFuncName(), label, elapsed)
   530  	return nil
   531  }
   532  
   533  // disableStorageProfile disables a storage profile while it is assigned to a provider VDC
   534  // Calling this function is a prerequisite to removing a storage profile from a provider VDC
   535  func disableStorageProfile(client *Client, storageProfileHref string) error {
   536  	disablePayload := &types.EnableStorageProfile{Enabled: false}
   537  	resp, err := client.executeJsonRequest(storageProfileHref, http.MethodPut, disablePayload,
   538  		"error disabling storage profile in provider VDC: %s")
   539  
   540  	defer closeBody(resp)
   541  	return err
   542  }
   543  
   544  // DeleteStorageProfiles removes storage profiles from the Provider VDC
   545  func (pvdc *ProviderVdcExtended) DeleteStorageProfiles(storageProfiles []string) error {
   546  	util.Logger.Printf("[TRACE] ProviderVdc.DeleteStorageProfiles")
   547  
   548  	href, err := url.JoinPath(pvdc.VMWProviderVdc.HREF, "storageProfiles")
   549  	if err != nil {
   550  		return err
   551  	}
   552  
   553  	usedStorageProfileRefs := pvdc.VMWProviderVdc.StorageProfiles.ProviderVdcStorageProfile
   554  
   555  	var toBeDeleted []*types.Reference
   556  
   557  	for _, sp := range storageProfiles {
   558  		var foundUsed bool
   559  		for _, usp := range usedStorageProfileRefs {
   560  			if sp == usp.Name {
   561  				foundUsed = true
   562  				toBeDeleted = append(toBeDeleted, &types.Reference{HREF: usp.HREF})
   563  				break
   564  			}
   565  		}
   566  		if !foundUsed {
   567  			return fmt.Errorf("storage profile %s not found in provider VDC %s", sp, pvdc.VMWProviderVdc.Name)
   568  		}
   569  	}
   570  
   571  	for _, sp := range toBeDeleted {
   572  		err = disableStorageProfile(pvdc.client, sp.HREF)
   573  		if err != nil {
   574  			return fmt.Errorf("error disabling storage profile %s from provider VDC %s: %s", sp.Name, pvdc.VMWProviderVdc.Name, err)
   575  		}
   576  	}
   577  	input := &types.RemoveStorageProfile{RemoveStorageProfile: toBeDeleted}
   578  
   579  	resp, err := pvdc.client.executeJsonRequest(href, http.MethodPost, input, "error removing storage profiles from provider VDC: %s")
   580  	if err != nil {
   581  		return err
   582  	}
   583  	defer closeBody(resp)
   584  	task := NewTask(pvdc.client)
   585  	err = decodeBody(types.BodyTypeJSON, resp, task.Task)
   586  	if err != nil {
   587  		return err
   588  	}
   589  	err = task.WaitTaskCompletion()
   590  	if err != nil {
   591  		return err
   592  	}
   593  	return pvdc.Refresh()
   594  }