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

     1  /*
     2   * Copyright 2022 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
     3   */
     4  
     5  package govcd
     6  
     7  import (
     8  	"fmt"
     9  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    10  	"github.com/vmware/go-vcloud-director/v2/util"
    11  	"net/http"
    12  	"regexp"
    13  	"strings"
    14  )
    15  
    16  // NOTE: This "v2" is not v2 in terms of API versioning, it's just a way to separate the functions that handle
    17  // metadata in a complete way (v2, this file) and the deprecated functions that were incomplete (v1, they lacked
    18  // "visibility" and "domain" handling).
    19  //
    20  // The idea is that once a new major version of go-vcloud-director is released, one can just remove "v1" file and perform
    21  // a minor refactoring of the code here (probably renaming functions). Also, the code in "v2" is organized differently,
    22  // as this is classified using "CRUD blocks" (meaning that all Create functions are together, same for Read... etc),
    23  // which makes the code more readable.
    24  
    25  // ------------------------------------------------------------------------------------------------
    26  // GET metadata by key
    27  // ------------------------------------------------------------------------------------------------
    28  
    29  // GetMetadataByKeyAndHref returns metadata from the given resource reference, corresponding to the given key and domain.
    30  func (vcdClient *VCDClient) GetMetadataByKeyAndHref(href, key string, isSystem bool) (*types.MetadataValue, error) {
    31  	return getMetadataByKey(&vcdClient.Client, href, "", key, isSystem)
    32  }
    33  
    34  // GetMetadataByKey returns VM metadata corresponding to the given key and domain.
    35  func (vm *VM) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    36  	return getMetadataByKey(vm.client, vm.VM.HREF, vm.VM.Name, key, isSystem)
    37  }
    38  
    39  // GetMetadataByKey returns VDC metadata corresponding to the given key and domain.
    40  func (vdc *Vdc) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    41  	return getMetadataByKey(vdc.client, vdc.Vdc.HREF, vdc.Vdc.Name, key, isSystem)
    42  }
    43  
    44  // GetMetadataByKey returns AdminVdc metadata corresponding to the given key and domain.
    45  func (adminVdc *AdminVdc) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    46  	return getMetadataByKey(adminVdc.client, adminVdc.AdminVdc.HREF, adminVdc.AdminVdc.Name, key, isSystem)
    47  }
    48  
    49  // GetMetadataByKey returns ProviderVdc metadata corresponding to the given key and domain.
    50  // Note: Requires system administrator privileges.
    51  func (providerVdc *ProviderVdc) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    52  	return getMetadataByKey(providerVdc.client, providerVdc.ProviderVdc.HREF, providerVdc.ProviderVdc.Name, key, isSystem)
    53  }
    54  
    55  // GetMetadataByKey returns VApp metadata corresponding to the given key and domain.
    56  func (vapp *VApp) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    57  	return getMetadataByKey(vapp.client, vapp.VApp.HREF, vapp.VApp.Name, key, isSystem)
    58  }
    59  
    60  // GetMetadataByKey returns VAppTemplate metadata corresponding to the given key and domain.
    61  func (vAppTemplate *VAppTemplate) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    62  	return getMetadataByKey(vAppTemplate.client, vAppTemplate.VAppTemplate.HREF, vAppTemplate.VAppTemplate.Name, key, isSystem)
    63  }
    64  
    65  // GetMetadataByKey returns MediaRecord metadata corresponding to the given key and domain.
    66  func (mediaRecord *MediaRecord) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    67  	return getMetadataByKey(mediaRecord.client, mediaRecord.MediaRecord.HREF, mediaRecord.MediaRecord.Name, key, isSystem)
    68  }
    69  
    70  // GetMetadataByKey returns Media metadata corresponding to the given key and domain.
    71  func (media *Media) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    72  	return getMetadataByKey(media.client, media.Media.HREF, media.Media.Name, key, isSystem)
    73  }
    74  
    75  // GetMetadataByKey returns Catalog metadata corresponding to the given key and domain.
    76  func (catalog *Catalog) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    77  	return getMetadataByKey(catalog.client, catalog.Catalog.HREF, catalog.Catalog.Name, key, isSystem)
    78  }
    79  
    80  // GetMetadataByKey returns AdminCatalog metadata corresponding to the given key and domain.
    81  func (adminCatalog *AdminCatalog) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    82  	return getMetadataByKey(adminCatalog.client, adminCatalog.AdminCatalog.HREF, adminCatalog.AdminCatalog.Name, key, isSystem)
    83  }
    84  
    85  // GetMetadataByKey returns the Org metadata corresponding to the given key and domain.
    86  func (org *Org) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    87  	return getMetadataByKey(org.client, org.Org.HREF, org.Org.Name, key, isSystem)
    88  }
    89  
    90  // GetMetadataByKey returns the AdminOrg metadata corresponding to the given key and domain.
    91  // Note: Requires system administrator privileges.
    92  func (adminOrg *AdminOrg) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    93  	return getMetadataByKey(adminOrg.client, adminOrg.AdminOrg.HREF, adminOrg.AdminOrg.Name, key, isSystem)
    94  }
    95  
    96  // GetMetadataByKey returns the metadata corresponding to the given key and domain.
    97  func (disk *Disk) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
    98  	return getMetadataByKey(disk.client, disk.Disk.HREF, disk.Disk.Name, key, isSystem)
    99  }
   100  
   101  // GetMetadataByKey returns OrgVDCNetwork metadata corresponding to the given key and domain.
   102  func (orgVdcNetwork *OrgVDCNetwork) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
   103  	return getMetadataByKey(orgVdcNetwork.client, orgVdcNetwork.OrgVDCNetwork.HREF, orgVdcNetwork.OrgVDCNetwork.Name, key, isSystem)
   104  }
   105  
   106  // GetMetadataByKey returns CatalogItem metadata corresponding to the given key and domain.
   107  func (catalogItem *CatalogItem) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
   108  	return getMetadataByKey(catalogItem.client, catalogItem.CatalogItem.HREF, catalogItem.CatalogItem.Name, key, isSystem)
   109  }
   110  
   111  // GetMetadataByKey returns OpenApiOrgVdcNetwork metadata corresponding to the given key and domain.
   112  // NOTE: This function cannot retrieve metadata if the network belongs to a VDC Group.
   113  // TODO: This function is currently using XML API underneath as OpenAPI metadata is still not supported.
   114  func (openApiOrgVdcNetwork *OpenApiOrgVdcNetwork) GetMetadataByKey(key string, isSystem bool) (*types.MetadataValue, error) {
   115  	href := fmt.Sprintf("%s/network/%s", openApiOrgVdcNetwork.client.VCDHREF.String(), extractUuid(openApiOrgVdcNetwork.OpenApiOrgVdcNetwork.ID))
   116  	return getMetadataByKey(openApiOrgVdcNetwork.client, href, openApiOrgVdcNetwork.OpenApiOrgVdcNetwork.Name, key, isSystem)
   117  }
   118  
   119  // ------------------------------------------------------------------------------------------------
   120  // GET all metadata
   121  // ------------------------------------------------------------------------------------------------
   122  
   123  // GetMetadataByHref returns metadata from the given resource reference.
   124  func (vcdClient *VCDClient) GetMetadataByHref(href string) (*types.Metadata, error) {
   125  	return getMetadata(&vcdClient.Client, href, "")
   126  }
   127  
   128  // GetMetadata returns VM metadata.
   129  func (vm *VM) GetMetadata() (*types.Metadata, error) {
   130  	return getMetadata(vm.client, vm.VM.HREF, vm.VM.Name)
   131  }
   132  
   133  // GetMetadata returns VDC metadata.
   134  func (vdc *Vdc) GetMetadata() (*types.Metadata, error) {
   135  	return getMetadata(vdc.client, vdc.Vdc.HREF, vdc.Vdc.Name)
   136  }
   137  
   138  // GetMetadata returns AdminVdc metadata.
   139  func (adminVdc *AdminVdc) GetMetadata() (*types.Metadata, error) {
   140  	return getMetadata(adminVdc.client, adminVdc.AdminVdc.HREF, adminVdc.AdminVdc.Name)
   141  }
   142  
   143  // GetMetadata returns ProviderVdc metadata.
   144  // Note: Requires system administrator privileges.
   145  func (providerVdc *ProviderVdc) GetMetadata() (*types.Metadata, error) {
   146  	return getMetadata(providerVdc.client, providerVdc.ProviderVdc.HREF, providerVdc.ProviderVdc.Name)
   147  }
   148  
   149  // GetMetadata returns VApp metadata.
   150  func (vapp *VApp) GetMetadata() (*types.Metadata, error) {
   151  	return getMetadata(vapp.client, vapp.VApp.HREF, vapp.VApp.Name)
   152  }
   153  
   154  // GetMetadata returns VAppTemplate metadata.
   155  func (vAppTemplate *VAppTemplate) GetMetadata() (*types.Metadata, error) {
   156  	return getMetadata(vAppTemplate.client, vAppTemplate.VAppTemplate.HREF, vAppTemplate.VAppTemplate.Name)
   157  }
   158  
   159  // GetMetadata returns MediaRecord metadata.
   160  func (mediaRecord *MediaRecord) GetMetadata() (*types.Metadata, error) {
   161  	return getMetadata(mediaRecord.client, mediaRecord.MediaRecord.HREF, mediaRecord.MediaRecord.Name)
   162  }
   163  
   164  // GetMetadata returns Media metadata.
   165  func (media *Media) GetMetadata() (*types.Metadata, error) {
   166  	return getMetadata(media.client, media.Media.HREF, media.Media.Name)
   167  }
   168  
   169  // GetMetadata returns Catalog metadata.
   170  func (catalog *Catalog) GetMetadata() (*types.Metadata, error) {
   171  	return getMetadata(catalog.client, catalog.Catalog.HREF, catalog.Catalog.Name)
   172  }
   173  
   174  // GetMetadata returns AdminCatalog metadata.
   175  func (adminCatalog *AdminCatalog) GetMetadata() (*types.Metadata, error) {
   176  	return getMetadata(adminCatalog.client, adminCatalog.AdminCatalog.HREF, adminCatalog.AdminCatalog.Name)
   177  }
   178  
   179  // GetMetadata returns the Org metadata of the corresponding organization seen as administrator
   180  func (org *Org) GetMetadata() (*types.Metadata, error) {
   181  	return getMetadata(org.client, org.Org.HREF, org.Org.Name)
   182  }
   183  
   184  // GetMetadata returns the AdminOrg metadata of the corresponding organization seen as administrator
   185  func (adminOrg *AdminOrg) GetMetadata() (*types.Metadata, error) {
   186  	return getMetadata(adminOrg.client, adminOrg.AdminOrg.HREF, adminOrg.AdminOrg.Name)
   187  }
   188  
   189  // GetMetadata returns the metadata of the corresponding independent disk
   190  func (disk *Disk) GetMetadata() (*types.Metadata, error) {
   191  	return getMetadata(disk.client, disk.Disk.HREF, disk.Disk.Name)
   192  }
   193  
   194  // GetMetadata returns OrgVDCNetwork metadata.
   195  func (orgVdcNetwork *OrgVDCNetwork) GetMetadata() (*types.Metadata, error) {
   196  	return getMetadata(orgVdcNetwork.client, orgVdcNetwork.OrgVDCNetwork.HREF, orgVdcNetwork.OrgVDCNetwork.Name)
   197  }
   198  
   199  // GetMetadata returns CatalogItem metadata.
   200  func (catalogItem *CatalogItem) GetMetadata() (*types.Metadata, error) {
   201  	return getMetadata(catalogItem.client, catalogItem.CatalogItem.HREF, catalogItem.CatalogItem.Name)
   202  }
   203  
   204  // GetMetadata returns OpenApiOrgVdcNetwork metadata.
   205  // NOTE: This function cannot retrieve metadata if the network belongs to a VDC Group.
   206  // TODO: This function is currently using XML API underneath as OpenAPI metadata is still not supported.
   207  func (openApiOrgVdcNetwork *OpenApiOrgVdcNetwork) GetMetadata() (*types.Metadata, error) {
   208  	href := fmt.Sprintf("%s/network/%s", openApiOrgVdcNetwork.client.VCDHREF.String(), extractUuid(openApiOrgVdcNetwork.OpenApiOrgVdcNetwork.ID))
   209  	return getMetadata(openApiOrgVdcNetwork.client, href, openApiOrgVdcNetwork.OpenApiOrgVdcNetwork.Name)
   210  }
   211  
   212  // ------------------------------------------------------------------------------------------------
   213  // ADD metadata async
   214  // ------------------------------------------------------------------------------------------------
   215  
   216  // AddMetadataEntryWithVisibilityByHrefAsync adds metadata to the given resource reference with the given key, value, type and visibility
   217  // and returns the task.
   218  func (vcdClient *VCDClient) AddMetadataEntryWithVisibilityByHrefAsync(href, key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   219  	return addMetadata(&vcdClient.Client, href, "", key, value, typedValue, visibility, isSystem)
   220  }
   221  
   222  // AddMetadataEntryWithVisibilityAsync adds metadata to the given VM with the given key, value, type and visibility
   223  // // and returns the task.
   224  func (vm *VM) AddMetadataEntryWithVisibilityAsync(key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   225  	return addMetadata(vm.client, vm.VM.HREF, vm.VM.Name, key, value, typedValue, visibility, isSystem)
   226  }
   227  
   228  // AddMetadataEntryWithVisibilityAsync adds metadata to the given AdminVdc with the given key, value, type and visibility
   229  // and returns the task.
   230  func (adminVdc *AdminVdc) AddMetadataEntryWithVisibilityAsync(key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   231  	return addMetadata(adminVdc.client, adminVdc.AdminVdc.HREF, adminVdc.AdminVdc.Name, key, value, typedValue, visibility, isSystem)
   232  }
   233  
   234  // AddMetadataEntryWithVisibilityAsync adds metadata to the given ProviderVdc with the given key, value, type and visibility
   235  // and returns the task.
   236  // Note: Requires system administrator privileges.
   237  func (providerVdc *ProviderVdc) AddMetadataEntryWithVisibilityAsync(key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   238  	return addMetadata(providerVdc.client, providerVdc.ProviderVdc.HREF, providerVdc.ProviderVdc.Name, key, value, typedValue, visibility, isSystem)
   239  }
   240  
   241  // AddMetadataEntryWithVisibilityAsync adds metadata to the given VApp with the given key, value, type and visibility
   242  // and returns the task.
   243  func (vapp *VApp) AddMetadataEntryWithVisibilityAsync(key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   244  	return addMetadata(vapp.client, vapp.VApp.HREF, vapp.VApp.Name, key, value, typedValue, visibility, isSystem)
   245  }
   246  
   247  // AddMetadataEntryWithVisibilityAsync adds metadata to the given VAppTemplate with the given key, value, type and visibility
   248  // and returns the task.
   249  func (vAppTemplate *VAppTemplate) AddMetadataEntryWithVisibilityAsync(key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   250  	return addMetadata(vAppTemplate.client, vAppTemplate.VAppTemplate.HREF, vAppTemplate.VAppTemplate.Name, key, value, typedValue, visibility, isSystem)
   251  }
   252  
   253  // AddMetadataEntryWithVisibilityAsync adds metadata to the given MediaRecord with the given key, value, type and visibility
   254  // and returns the task.
   255  func (mediaRecord *MediaRecord) AddMetadataEntryWithVisibilityAsync(key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   256  	return addMetadata(mediaRecord.client, mediaRecord.MediaRecord.HREF, mediaRecord.MediaRecord.Name, key, value, typedValue, visibility, isSystem)
   257  }
   258  
   259  // AddMetadataEntryWithVisibilityAsync adds metadata to the given Media with the given key, value, type and visibility
   260  // and returns the task.
   261  func (media *Media) AddMetadataEntryWithVisibilityAsync(key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   262  	return addMetadata(media.client, media.Media.HREF, media.Media.Name, key, value, typedValue, visibility, isSystem)
   263  }
   264  
   265  // AddMetadataEntryWithVisibilityAsync adds metadata to the given AdminCatalog with the given key, value, type and visibility
   266  // and returns the task.
   267  func (adminCatalog *AdminCatalog) AddMetadataEntryWithVisibilityAsync(key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   268  	return addMetadata(adminCatalog.client, adminCatalog.AdminCatalog.HREF, adminCatalog.AdminCatalog.Name, key, value, typedValue, visibility, isSystem)
   269  }
   270  
   271  // AddMetadataEntryWithVisibilityAsync adds metadata to the given AdminOrg with the given key, value, type and visibility
   272  // and returns the task.
   273  func (adminOrg *AdminOrg) AddMetadataEntryWithVisibilityAsync(key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   274  	return addMetadata(adminOrg.client, adminOrg.AdminOrg.HREF, adminOrg.AdminOrg.Name, key, value, typedValue, visibility, isSystem)
   275  }
   276  
   277  // AddMetadataEntryWithVisibilityAsync adds metadata to the given Disk with the given key, value, type and visibility
   278  // and returns the task.
   279  func (disk *Disk) AddMetadataEntryWithVisibilityAsync(key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   280  	return addMetadata(disk.client, disk.Disk.HREF, disk.Disk.Name, key, value, typedValue, visibility, isSystem)
   281  }
   282  
   283  // AddMetadataEntryWithVisibilityAsync adds metadata to the given OrgVDCNetwork with the given key, value, type and visibility
   284  // and returns the task.
   285  // Note: Requires system administrator privileges.
   286  func (orgVdcNetwork *OrgVDCNetwork) AddMetadataEntryWithVisibilityAsync(key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   287  	return addMetadata(orgVdcNetwork.client, getAdminURL(orgVdcNetwork.OrgVDCNetwork.HREF), orgVdcNetwork.OrgVDCNetwork.Name, key, value, typedValue, visibility, isSystem)
   288  }
   289  
   290  // AddMetadataEntryWithVisibilityAsync adds metadata to the given Catalog Item with the given key, value, type and visibility
   291  // and returns the task.
   292  func (catalogItem *CatalogItem) AddMetadataEntryWithVisibilityAsync(key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   293  	return addMetadata(catalogItem.client, catalogItem.CatalogItem.HREF, catalogItem.CatalogItem.Name, key, value, typedValue, visibility, isSystem)
   294  }
   295  
   296  // ------------------------------------------------------------------------------------------------
   297  // ADD metadata
   298  // ------------------------------------------------------------------------------------------------
   299  
   300  // AddMetadataEntryWithVisibilityByHref adds metadata to the given resource reference with the given key, value, type and visibility
   301  // and waits for completion.
   302  func (vcdClient *VCDClient) AddMetadataEntryWithVisibilityByHref(href, key, value, typedValue, visibility string, isSystem bool) error {
   303  	task, err := vcdClient.AddMetadataEntryWithVisibilityByHrefAsync(href, key, value, typedValue, visibility, isSystem)
   304  	if err != nil {
   305  		return err
   306  	}
   307  	return task.WaitTaskCompletion()
   308  }
   309  
   310  // AddMetadataEntryWithVisibility adds metadata to the receiver VM and waits for the task to finish.
   311  func (vm *VM) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   312  	return addMetadataAndWait(vm.client, vm.VM.HREF, vm.VM.Name, key, value, typedValue, visibility, isSystem)
   313  }
   314  
   315  // AddMetadataEntryWithVisibility adds metadata to the receiver AdminVdc and waits for the task to finish.
   316  func (adminVdc *AdminVdc) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   317  	return addMetadataAndWait(adminVdc.client, adminVdc.AdminVdc.HREF, adminVdc.AdminVdc.Name, key, value, typedValue, visibility, isSystem)
   318  }
   319  
   320  // AddMetadataEntryWithVisibility adds metadata to the receiver ProviderVdc and waits for the task to finish.
   321  // Note: Requires system administrator privileges.
   322  func (providerVdc *ProviderVdc) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   323  	return addMetadataAndWait(providerVdc.client, providerVdc.ProviderVdc.HREF, providerVdc.ProviderVdc.Name, key, value, typedValue, visibility, isSystem)
   324  }
   325  
   326  // AddMetadataEntryWithVisibility adds metadata to the receiver VApp and waits for the task to finish.
   327  func (vapp *VApp) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   328  	return addMetadataAndWait(vapp.client, vapp.VApp.HREF, vapp.VApp.Name, key, value, typedValue, visibility, isSystem)
   329  }
   330  
   331  // AddMetadataEntryWithVisibility adds metadata to the receiver VAppTemplate and waits for the task to finish.
   332  func (vAppTemplate *VAppTemplate) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   333  	return addMetadataAndWait(vAppTemplate.client, vAppTemplate.VAppTemplate.HREF, vAppTemplate.VAppTemplate.Name, key, value, typedValue, visibility, isSystem)
   334  }
   335  
   336  // AddMetadataEntryWithVisibility adds metadata to the receiver MediaRecord and waits for the task to finish.
   337  func (mediaRecord *MediaRecord) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   338  	return addMetadataAndWait(mediaRecord.client, mediaRecord.MediaRecord.HREF, mediaRecord.MediaRecord.Name, key, value, typedValue, visibility, isSystem)
   339  }
   340  
   341  // AddMetadataEntryWithVisibility adds metadata to the receiver Media and waits for the task to finish.
   342  func (media *Media) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   343  	return addMetadataAndWait(media.client, media.Media.HREF, media.Media.Name, key, value, typedValue, visibility, isSystem)
   344  }
   345  
   346  // AddMetadataEntryWithVisibility adds metadata to the receiver AdminCatalog and waits for the task to finish.
   347  func (adminCatalog *AdminCatalog) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   348  	return addMetadataAndWait(adminCatalog.client, adminCatalog.AdminCatalog.HREF, adminCatalog.AdminCatalog.Name, key, value, typedValue, visibility, isSystem)
   349  }
   350  
   351  // AddMetadataEntryWithVisibility adds metadata to the receiver AdminOrg and waits for the task to finish.
   352  func (adminOrg *AdminOrg) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   353  	return addMetadataAndWait(adminOrg.client, adminOrg.AdminOrg.HREF, adminOrg.AdminOrg.Name, key, value, typedValue, visibility, isSystem)
   354  }
   355  
   356  // AddMetadataEntryWithVisibility adds metadata to the receiver Disk and waits for the task to finish.
   357  func (disk *Disk) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   358  	return addMetadataAndWait(disk.client, disk.Disk.HREF, disk.Disk.Name, key, value, typedValue, visibility, isSystem)
   359  }
   360  
   361  // AddMetadataEntryWithVisibility adds metadata to the receiver OrgVDCNetwork and waits for the task to finish.
   362  // Note: Requires system administrator privileges.
   363  func (orgVdcNetwork *OrgVDCNetwork) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   364  	return addMetadataAndWait(orgVdcNetwork.client, getAdminURL(orgVdcNetwork.OrgVDCNetwork.HREF), orgVdcNetwork.OrgVDCNetwork.Name, key, value, typedValue, visibility, isSystem)
   365  }
   366  
   367  // AddMetadataEntryWithVisibility adds metadata to the receiver CatalogItem and waits for the task to finish.
   368  func (catalogItem *CatalogItem) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   369  	return addMetadataAndWait(catalogItem.client, catalogItem.CatalogItem.HREF, catalogItem.CatalogItem.Name, key, value, typedValue, visibility, isSystem)
   370  }
   371  
   372  // AddMetadataEntryWithVisibility adds metadata to the receiver OpenApiOrgVdcNetwork and waits for the task to finish.
   373  // Note: It doesn't add metadata to networks that belong to a VDC Group.
   374  // TODO: This function is currently using XML API underneath as OpenAPI metadata is still not supported.
   375  func (openApiOrgVdcNetwork *OpenApiOrgVdcNetwork) AddMetadataEntryWithVisibility(key, value, typedValue, visibility string, isSystem bool) error {
   376  	href := fmt.Sprintf("%s/admin/network/%s", openApiOrgVdcNetwork.client.VCDHREF.String(), extractUuid(openApiOrgVdcNetwork.OpenApiOrgVdcNetwork.ID))
   377  	task, err := addMetadata(openApiOrgVdcNetwork.client, href, openApiOrgVdcNetwork.OpenApiOrgVdcNetwork.Name, key, value, typedValue, visibility, isSystem)
   378  	if err != nil {
   379  		return err
   380  	}
   381  	return task.WaitTaskCompletion()
   382  }
   383  
   384  // ------------------------------------------------------------------------------------------------
   385  // MERGE metadata async
   386  // ------------------------------------------------------------------------------------------------
   387  
   388  // MergeMetadataWithVisibilityByHrefAsync updates the metadata entries present in the referenced entity and creates the ones not present, then
   389  // returns the task.
   390  func (vcdClient *VCDClient) MergeMetadataWithVisibilityByHrefAsync(href string, metadata map[string]types.MetadataValue) (Task, error) {
   391  	return mergeAllMetadata(&vcdClient.Client, href, "", metadata)
   392  }
   393  
   394  // MergeMetadataWithMetadataValuesAsync merges VM metadata provided as a key-value map of type `typedValue` with the already present in VCD,
   395  // then returns the task.
   396  func (vm *VM) MergeMetadataWithMetadataValuesAsync(metadata map[string]types.MetadataValue) (Task, error) {
   397  	return mergeAllMetadata(vm.client, vm.VM.HREF, vm.VM.Name, metadata)
   398  }
   399  
   400  // MergeMetadataWithMetadataValuesAsync merges AdminVdc metadata provided as a key-value map of type `typedValue` with the already present in VCD,
   401  // then waits for the task to complete.
   402  func (adminVdc *AdminVdc) MergeMetadataWithMetadataValuesAsync(metadata map[string]types.MetadataValue) (Task, error) {
   403  	return mergeAllMetadata(adminVdc.client, adminVdc.AdminVdc.HREF, adminVdc.AdminVdc.Name, metadata)
   404  }
   405  
   406  // MergeMetadataWithMetadataValuesAsync merges Provider VDC metadata provided as a key-value map of type `typedValue` with the already present in VCD,
   407  // then waits for the task to complete.
   408  // Note: Requires system administrator privileges.
   409  func (providerVdc *ProviderVdc) MergeMetadataWithMetadataValuesAsync(metadata map[string]types.MetadataValue) (Task, error) {
   410  	return mergeAllMetadata(providerVdc.client, providerVdc.ProviderVdc.HREF, providerVdc.ProviderVdc.Name, metadata)
   411  }
   412  
   413  // MergeMetadataWithMetadataValuesAsync merges VApp metadata provided as a key-value map of type `typedValue` with the already present in VCD,
   414  // then waits for the task to complete.
   415  func (vapp *VApp) MergeMetadataWithMetadataValuesAsync(metadata map[string]types.MetadataValue) (Task, error) {
   416  	return mergeAllMetadata(vapp.client, vapp.VApp.HREF, vapp.VApp.Name, metadata)
   417  }
   418  
   419  // MergeMetadataWithMetadataValuesAsync merges VAppTemplate metadata provided as a key-value map of type `typedValue` with the already present in VCD,
   420  // then waits for the task to complete.
   421  func (vAppTemplate *VAppTemplate) MergeMetadataWithMetadataValuesAsync(metadata map[string]types.MetadataValue) (Task, error) {
   422  	return mergeAllMetadata(vAppTemplate.client, vAppTemplate.VAppTemplate.HREF, vAppTemplate.VAppTemplate.Name, metadata)
   423  }
   424  
   425  // MergeMetadataWithMetadataValuesAsync merges MediaRecord metadata provided as a key-value map of type `typedValue` with the already present in VCD,
   426  // then waits for the task to complete.
   427  func (mediaRecord *MediaRecord) MergeMetadataWithMetadataValuesAsync(metadata map[string]types.MetadataValue) (Task, error) {
   428  	return mergeAllMetadata(mediaRecord.client, mediaRecord.MediaRecord.HREF, mediaRecord.MediaRecord.Name, metadata)
   429  }
   430  
   431  // MergeMetadataWithMetadataValuesAsync merges Media metadata provided as a key-value map of type `typedValue` with the already present in VCD,
   432  // then waits for the task to complete.
   433  func (media *Media) MergeMetadataWithMetadataValuesAsync(metadata map[string]types.MetadataValue) (Task, error) {
   434  	return mergeAllMetadata(media.client, media.Media.HREF, media.Media.Name, metadata)
   435  }
   436  
   437  // MergeMetadataWithMetadataValuesAsync merges AdminCatalog metadata provided as a key-value map of type `typedValue` with the already present in VCD,
   438  // then waits for the task to complete.
   439  func (adminCatalog *AdminCatalog) MergeMetadataWithMetadataValuesAsync(metadata map[string]types.MetadataValue) (Task, error) {
   440  	return mergeAllMetadata(adminCatalog.client, adminCatalog.AdminCatalog.HREF, adminCatalog.AdminCatalog.Name, metadata)
   441  }
   442  
   443  // MergeMetadataWithMetadataValuesAsync merges AdminOrg metadata provided as a key-value map of type `typedValue` with the already present in VCD,
   444  // then waits for the task to complete.
   445  func (adminOrg *AdminOrg) MergeMetadataWithMetadataValuesAsync(metadata map[string]types.MetadataValue) (Task, error) {
   446  	return mergeAllMetadata(adminOrg.client, adminOrg.AdminOrg.HREF, adminOrg.AdminOrg.Name, metadata)
   447  }
   448  
   449  // MergeMetadataWithMetadataValuesAsync merges Disk metadata provided as a key-value map of type `typedValue` with the already present in VCD,
   450  // then waits for the task to complete.
   451  func (disk *Disk) MergeMetadataWithMetadataValuesAsync(metadata map[string]types.MetadataValue) (Task, error) {
   452  	return mergeAllMetadata(disk.client, disk.Disk.HREF, disk.Disk.Name, metadata)
   453  }
   454  
   455  // MergeMetadataWithMetadataValuesAsync merges OrgVDCNetwork metadata provided as a key-value map of type `typedValue` with the already present in VCD,
   456  // then waits for the task to complete.
   457  // Note: Requires system administrator privileges.
   458  func (orgVdcNetwork *OrgVDCNetwork) MergeMetadataWithMetadataValuesAsync(metadata map[string]types.MetadataValue) (Task, error) {
   459  	return mergeAllMetadata(orgVdcNetwork.client, getAdminURL(orgVdcNetwork.OrgVDCNetwork.HREF), orgVdcNetwork.OrgVDCNetwork.Name, metadata)
   460  }
   461  
   462  // MergeMetadataWithMetadataValuesAsync merges CatalogItem metadata provided as a key-value map of type `typedValue` with the already present in VCD,
   463  // then waits for the task to complete.
   464  func (catalogItem *CatalogItem) MergeMetadataWithMetadataValuesAsync(metadata map[string]types.MetadataValue) (Task, error) {
   465  	return mergeAllMetadata(catalogItem.client, catalogItem.CatalogItem.HREF, catalogItem.CatalogItem.Name, metadata)
   466  }
   467  
   468  // ------------------------------------------------------------------------------------------------
   469  // MERGE metadata
   470  // ------------------------------------------------------------------------------------------------
   471  
   472  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver VM and creates the ones not present.
   473  // The input metadata map has a "metadata key"->"metadata value" relation.
   474  // This function waits until merge finishes.
   475  func (vm *VM) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   476  	return mergeMetadataAndWait(vm.client, vm.VM.HREF, vm.VM.Name, metadata)
   477  }
   478  
   479  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver AdminVdc and creates the ones not present.
   480  // The input metadata map has a "metadata key"->"metadata value" relation.
   481  // This function waits until merge finishes.
   482  func (adminVdc *AdminVdc) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   483  	return mergeMetadataAndWait(adminVdc.client, adminVdc.AdminVdc.HREF, adminVdc.AdminVdc.Name, metadata)
   484  }
   485  
   486  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver ProviderVdc and creates the ones not present.
   487  // The input metadata map has a "metadata key"->"metadata value" relation.
   488  // This function waits until merge finishes.
   489  // Note: Requires system administrator privileges.
   490  func (providerVdc *ProviderVdc) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   491  	return mergeMetadataAndWait(providerVdc.client, providerVdc.ProviderVdc.HREF, providerVdc.ProviderVdc.Name, metadata)
   492  }
   493  
   494  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver VApp and creates the ones not present.
   495  // The input metadata map has a "metadata key"->"metadata value" relation.
   496  // This function waits until merge finishes.
   497  func (vApp *VApp) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   498  	return mergeMetadataAndWait(vApp.client, vApp.VApp.HREF, vApp.VApp.Name, metadata)
   499  }
   500  
   501  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver VAppTemplate and creates the ones not present.
   502  // The input metadata map has a "metadata key"->"metadata value" relation.
   503  // This function waits until merge finishes.
   504  func (vAppTemplate *VAppTemplate) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   505  	return mergeMetadataAndWait(vAppTemplate.client, vAppTemplate.VAppTemplate.HREF, vAppTemplate.VAppTemplate.Name, metadata)
   506  }
   507  
   508  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver MediaRecord and creates the ones not present.
   509  // The input metadata map has a "metadata key"->"metadata value" relation.
   510  // This function waits until merge finishes.
   511  func (mediaRecord *MediaRecord) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   512  	return mergeMetadataAndWait(mediaRecord.client, mediaRecord.MediaRecord.HREF, mediaRecord.MediaRecord.Name, metadata)
   513  }
   514  
   515  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver Media and creates the ones not present.
   516  // The input metadata map has a "metadata key"->"metadata value" relation.
   517  // This function waits until merge finishes.
   518  func (media *Media) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   519  	return mergeMetadataAndWait(media.client, media.Media.HREF, media.Media.Name, metadata)
   520  }
   521  
   522  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver AdminCatalog and creates the ones not present.
   523  // The input metadata map has a "metadata key"->"metadata value" relation.
   524  // This function waits until merge finishes.
   525  func (adminCatalog *AdminCatalog) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   526  	return mergeMetadataAndWait(adminCatalog.client, adminCatalog.AdminCatalog.HREF, adminCatalog.AdminCatalog.Name, metadata)
   527  }
   528  
   529  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver AdminOrg and creates the ones not present.
   530  // The input metadata map has a "metadata key"->"metadata value" relation.
   531  // This function waits until merge finishes.
   532  func (adminOrg *AdminOrg) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   533  	return mergeMetadataAndWait(adminOrg.client, adminOrg.AdminOrg.HREF, adminOrg.AdminOrg.Name, metadata)
   534  }
   535  
   536  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver Disk and creates the ones not present.
   537  // The input metadata map has a "metadata key"->"metadata value" relation.
   538  // This function waits until merge finishes.
   539  func (disk *Disk) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   540  	return mergeMetadataAndWait(disk.client, disk.Disk.HREF, disk.Disk.Name, metadata)
   541  }
   542  
   543  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver OrgVDCNetwork and creates the ones not present.
   544  // The input metadata map has a "metadata key"->"metadata value" relation.
   545  // This function waits until merge finishes.
   546  // Note: Requires system administrator privileges.
   547  func (orgVdcNetwork *OrgVDCNetwork) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   548  	return mergeMetadataAndWait(orgVdcNetwork.client, getAdminURL(orgVdcNetwork.OrgVDCNetwork.HREF), orgVdcNetwork.OrgVDCNetwork.Name, metadata)
   549  }
   550  
   551  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver CatalogItem and creates the ones not present.
   552  // The input metadata map has a "metadata key"->"metadata value" relation.
   553  // This function waits until merge finishes.
   554  func (catalogItem *CatalogItem) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   555  	return mergeMetadataAndWait(catalogItem.client, catalogItem.CatalogItem.HREF, catalogItem.CatalogItem.Name, metadata)
   556  }
   557  
   558  // MergeMetadataWithMetadataValues updates the metadata values that are already present in the receiver OpenApiOrgVdcNetwork and creates the ones not present.
   559  // The input metadata map has a "metadata key"->"metadata value" relation.
   560  // This function waits until merge finishes.
   561  // Note: It doesn't merge metadata to networks that belong to a VDC Group.
   562  // TODO: This function is currently using XML API underneath as OpenAPI metadata is still not supported.
   563  func (openApiOrgVdcNetwork *OpenApiOrgVdcNetwork) MergeMetadataWithMetadataValues(metadata map[string]types.MetadataValue) error {
   564  	href := fmt.Sprintf("%s/admin/network/%s", openApiOrgVdcNetwork.client.VCDHREF.String(), extractUuid(openApiOrgVdcNetwork.OpenApiOrgVdcNetwork.ID))
   565  	task, err := mergeAllMetadata(openApiOrgVdcNetwork.client, href, openApiOrgVdcNetwork.OpenApiOrgVdcNetwork.Name, metadata)
   566  	if err != nil {
   567  		return err
   568  	}
   569  	return task.WaitTaskCompletion()
   570  }
   571  
   572  // ------------------------------------------------------------------------------------------------
   573  // DELETE metadata async
   574  // ------------------------------------------------------------------------------------------------
   575  
   576  // DeleteMetadataEntryWithDomainByHrefAsync deletes metadata from the given resource reference, depending on key provided as input
   577  // and returns a task.
   578  func (vcdClient *VCDClient) DeleteMetadataEntryWithDomainByHrefAsync(href, key string, isSystem bool) (Task, error) {
   579  	return deleteMetadata(&vcdClient.Client, href, "", key, isSystem)
   580  }
   581  
   582  // DeleteMetadataEntryWithDomainAsync deletes VM metadata associated to the input key and returns the task.
   583  func (vm *VM) DeleteMetadataEntryWithDomainAsync(key string, isSystem bool) (Task, error) {
   584  	return deleteMetadata(vm.client, vm.VM.HREF, vm.VM.Name, key, isSystem)
   585  }
   586  
   587  // DeleteMetadataEntryWithDomainAsync deletes AdminVdc metadata associated to the input key and returns the task.
   588  func (adminVdc *AdminVdc) DeleteMetadataEntryWithDomainAsync(key string, isSystem bool) (Task, error) {
   589  	return deleteMetadata(adminVdc.client, adminVdc.AdminVdc.HREF, adminVdc.AdminVdc.Name, key, isSystem)
   590  }
   591  
   592  // DeleteMetadataEntryWithDomainAsync deletes ProviderVdc metadata associated to the input key and returns the task.
   593  // Note: Requires system administrator privileges.
   594  func (providerVdc *ProviderVdc) DeleteMetadataEntryWithDomainAsync(key string, isSystem bool) (Task, error) {
   595  	return deleteMetadata(providerVdc.client, providerVdc.ProviderVdc.HREF, providerVdc.ProviderVdc.Name, key, isSystem)
   596  }
   597  
   598  // DeleteMetadataEntryWithDomainAsync deletes VApp metadata associated to the input key and returns the task.
   599  func (vapp *VApp) DeleteMetadataEntryWithDomainAsync(key string, isSystem bool) (Task, error) {
   600  	return deleteMetadata(vapp.client, vapp.VApp.HREF, vapp.VApp.Name, key, isSystem)
   601  }
   602  
   603  // DeleteMetadataEntryWithDomainAsync deletes VAppTemplate metadata associated to the input key and returns the task.
   604  func (vAppTemplate *VAppTemplate) DeleteMetadataEntryWithDomainAsync(key string, isSystem bool) (Task, error) {
   605  	return deleteMetadata(vAppTemplate.client, vAppTemplate.VAppTemplate.HREF, vAppTemplate.VAppTemplate.Name, key, isSystem)
   606  }
   607  
   608  // DeleteMetadataEntryWithDomainAsync deletes MediaRecord metadata associated to the input key and returns the task.
   609  func (mediaRecord *MediaRecord) DeleteMetadataEntryWithDomainAsync(key string, isSystem bool) (Task, error) {
   610  	return deleteMetadata(mediaRecord.client, mediaRecord.MediaRecord.HREF, mediaRecord.MediaRecord.Name, key, isSystem)
   611  }
   612  
   613  // DeleteMetadataEntryWithDomainAsync deletes Media metadata associated to the input key and returns the task.
   614  func (media *Media) DeleteMetadataEntryWithDomainAsync(key string, isSystem bool) (Task, error) {
   615  	return deleteMetadata(media.client, media.Media.HREF, media.Media.Name, key, isSystem)
   616  }
   617  
   618  // DeleteMetadataEntryWithDomainAsync deletes AdminCatalog metadata associated to the input key and returns the task.
   619  func (adminCatalog *AdminCatalog) DeleteMetadataEntryWithDomainAsync(key string, isSystem bool) (Task, error) {
   620  	return deleteMetadata(adminCatalog.client, adminCatalog.AdminCatalog.HREF, adminCatalog.AdminCatalog.Name, key, isSystem)
   621  }
   622  
   623  // DeleteMetadataEntryWithDomainAsync deletes AdminOrg metadata associated to the input key and returns the task.
   624  func (adminOrg *AdminOrg) DeleteMetadataEntryWithDomainAsync(key string, isSystem bool) (Task, error) {
   625  	return deleteMetadata(adminOrg.client, adminOrg.AdminOrg.HREF, adminOrg.AdminOrg.Name, key, isSystem)
   626  }
   627  
   628  // DeleteMetadataEntryWithDomainAsync deletes Disk metadata associated to the input key and returns the task.
   629  func (disk *Disk) DeleteMetadataEntryWithDomainAsync(key string, isSystem bool) (Task, error) {
   630  	return deleteMetadata(disk.client, disk.Disk.HREF, disk.Disk.Name, key, isSystem)
   631  }
   632  
   633  // DeleteMetadataEntryWithDomainAsync deletes OrgVDCNetwork metadata associated to the input key and returns the task.
   634  // Note: Requires system administrator privileges.
   635  func (orgVdcNetwork *OrgVDCNetwork) DeleteMetadataEntryWithDomainAsync(key string, isSystem bool) (Task, error) {
   636  	return deleteMetadata(orgVdcNetwork.client, getAdminURL(orgVdcNetwork.OrgVDCNetwork.HREF), orgVdcNetwork.OrgVDCNetwork.Name, key, isSystem)
   637  }
   638  
   639  // DeleteMetadataEntryWithDomainAsync deletes CatalogItem metadata associated to the input key and returns the task.
   640  func (catalogItem *CatalogItem) DeleteMetadataEntryWithDomainAsync(key string, isSystem bool) (Task, error) {
   641  	return deleteMetadata(catalogItem.client, catalogItem.CatalogItem.HREF, catalogItem.CatalogItem.Name, key, isSystem)
   642  }
   643  
   644  // ------------------------------------------------------------------------------------------------
   645  // DELETE metadata
   646  // ------------------------------------------------------------------------------------------------
   647  
   648  // DeleteMetadataEntryWithDomainByHref deletes metadata from the given resource reference, depending on key provided as input
   649  // and waits for the task to finish.
   650  func (vcdClient *VCDClient) DeleteMetadataEntryWithDomainByHref(href, key string, isSystem bool) error {
   651  	task, err := vcdClient.DeleteMetadataEntryWithDomainByHrefAsync(href, key, isSystem)
   652  	if err != nil {
   653  		return err
   654  	}
   655  	return task.WaitTaskCompletion()
   656  }
   657  
   658  // DeleteMetadataEntryWithDomain deletes VM metadata associated to the input key and waits for the task to finish.
   659  func (vm *VM) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   660  	return deleteMetadataAndWait(vm.client, vm.VM.HREF, vm.VM.Name, key, isSystem)
   661  }
   662  
   663  // DeleteMetadataEntryWithDomain deletes AdminVdc metadata associated to the input key and waits for the task to finish.
   664  // Note: Requires system administrator privileges.
   665  func (adminVdc *AdminVdc) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   666  	return deleteMetadataAndWait(adminVdc.client, getAdminURL(adminVdc.AdminVdc.HREF), adminVdc.AdminVdc.Name, key, isSystem)
   667  }
   668  
   669  // DeleteMetadataEntryWithDomain deletes ProviderVdc metadata associated to the input key and waits for the task to finish.
   670  // Note: Requires system administrator privileges.
   671  func (providerVdc *ProviderVdc) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   672  	return deleteMetadataAndWait(providerVdc.client, providerVdc.ProviderVdc.HREF, providerVdc.ProviderVdc.Name, key, isSystem)
   673  }
   674  
   675  // DeleteMetadataEntryWithDomain deletes VApp metadata associated to the input key and waits for the task to finish.
   676  func (vApp *VApp) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   677  	return deleteMetadataAndWait(vApp.client, vApp.VApp.HREF, vApp.VApp.Name, key, isSystem)
   678  }
   679  
   680  // DeleteMetadataEntryWithDomain deletes VAppTemplate metadata associated to the input key and waits for the task to finish.
   681  func (vAppTemplate *VAppTemplate) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   682  	return deleteMetadataAndWait(vAppTemplate.client, vAppTemplate.VAppTemplate.HREF, vAppTemplate.VAppTemplate.Name, key, isSystem)
   683  }
   684  
   685  // DeleteMetadataEntryWithDomain deletes MediaRecord metadata associated to the input key and waits for the task to finish.
   686  func (mediaRecord *MediaRecord) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   687  	return deleteMetadataAndWait(mediaRecord.client, mediaRecord.MediaRecord.HREF, mediaRecord.MediaRecord.Name, key, isSystem)
   688  }
   689  
   690  // DeleteMetadataEntryWithDomain deletes Media metadata associated to the input key and waits for the task to finish.
   691  func (media *Media) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   692  	return deleteMetadataAndWait(media.client, media.Media.HREF, media.Media.Name, key, isSystem)
   693  }
   694  
   695  // DeleteMetadataEntryWithDomain deletes AdminCatalog metadata associated to the input key and waits for the task to finish.
   696  func (adminCatalog *AdminCatalog) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   697  	return deleteMetadataAndWait(adminCatalog.client, adminCatalog.AdminCatalog.HREF, adminCatalog.AdminCatalog.Name, key, isSystem)
   698  }
   699  
   700  // DeleteMetadataEntryWithDomain deletes AdminOrg metadata associated to the input key and waits for the task to finish.
   701  func (adminOrg *AdminOrg) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   702  	return deleteMetadataAndWait(adminOrg.client, adminOrg.AdminOrg.HREF, adminOrg.AdminOrg.Name, key, isSystem)
   703  }
   704  
   705  // DeleteMetadataEntryWithDomain deletes Disk metadata associated to the input key and waits for the task to finish.
   706  func (disk *Disk) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   707  	return deleteMetadataAndWait(disk.client, disk.Disk.HREF, disk.Disk.Name, key, isSystem)
   708  }
   709  
   710  // DeleteMetadataEntryWithDomain deletes OrgVDCNetwork metadata associated to the input key and waits for the task to finish.
   711  // Note: Requires system administrator privileges.
   712  func (orgVdcNetwork *OrgVDCNetwork) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   713  	return deleteMetadataAndWait(orgVdcNetwork.client, getAdminURL(orgVdcNetwork.OrgVDCNetwork.HREF), orgVdcNetwork.OrgVDCNetwork.Name, key, isSystem)
   714  }
   715  
   716  // DeleteMetadataEntryWithDomain deletes CatalogItem metadata associated to the input key and waits for the task to finish.
   717  func (catalogItem *CatalogItem) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   718  	return deleteMetadataAndWait(catalogItem.client, catalogItem.CatalogItem.HREF, catalogItem.CatalogItem.Name, key, isSystem)
   719  }
   720  
   721  // DeleteMetadataEntryWithDomain deletes OpenApiOrgVdcNetwork metadata associated to the input key and waits for the task to finish.
   722  // Note: It doesn't delete metadata from networks that belong to a VDC Group.
   723  // TODO: This function is currently using XML API underneath as OpenAPI metadata is still not supported.
   724  func (openApiOrgVdcNetwork *OpenApiOrgVdcNetwork) DeleteMetadataEntryWithDomain(key string, isSystem bool) error {
   725  	href := fmt.Sprintf("%s/admin/network/%s", openApiOrgVdcNetwork.client.VCDHREF.String(), extractUuid(openApiOrgVdcNetwork.OpenApiOrgVdcNetwork.ID))
   726  	task, err := deleteMetadata(openApiOrgVdcNetwork.client, href, openApiOrgVdcNetwork.OpenApiOrgVdcNetwork.Name, key, isSystem)
   727  	if err != nil {
   728  		return err
   729  	}
   730  	return task.WaitTaskCompletion()
   731  }
   732  
   733  // ------------------------------------------------------------------------------------------------
   734  // Ignored metadata set/unset
   735  // ------------------------------------------------------------------------------------------------
   736  
   737  // SetMetadataToIgnore allows to update the metadata to be ignored in all metadata API calls with
   738  // the given input. It returns the old IgnoredMetadata configuration from the client
   739  func (vcdClient *VCDClient) SetMetadataToIgnore(ignoredMetadata []IgnoredMetadata) []IgnoredMetadata {
   740  	result := vcdClient.Client.IgnoredMetadata
   741  	vcdClient.Client.IgnoredMetadata = ignoredMetadata
   742  	return result
   743  }
   744  
   745  // ------------------------------------------------------------------------------------------------
   746  // Generic private functions
   747  // ------------------------------------------------------------------------------------------------
   748  
   749  // getMetadata is a generic function to retrieve metadata from VCD
   750  func getMetadataByKey(client *Client, requestUri, name, key string, isSystem bool) (*types.MetadataValue, error) {
   751  	metadata := &types.MetadataValue{}
   752  	href := requestUri + "/metadata/"
   753  
   754  	if isSystem {
   755  		href += "SYSTEM/"
   756  	}
   757  
   758  	_, err := client.ExecuteRequest(href+key, http.MethodGet, types.MimeMetaData, "error retrieving metadata by key "+key+": %s", nil, metadata)
   759  	if err != nil {
   760  		return nil, err
   761  	}
   762  	return filterSingleXmlMetadataEntry(key, requestUri, name, metadata, client.IgnoredMetadata)
   763  }
   764  
   765  // getMetadata is a generic function to retrieve metadata from VCD
   766  func getMetadata(client *Client, requestUri, name string) (*types.Metadata, error) {
   767  	metadata := &types.Metadata{}
   768  
   769  	_, err := client.ExecuteRequest(requestUri+"/metadata/", http.MethodGet, types.MimeMetaData, "error retrieving metadata: %s", nil, metadata)
   770  	if err != nil {
   771  		return nil, err
   772  	}
   773  	return filterXmlMetadata(metadata, requestUri, name, client.IgnoredMetadata)
   774  }
   775  
   776  // addMetadata adds metadata to an entity.
   777  // If the metadata entry is of the SYSTEM domain (isSystem=true), one can set different types of Visibility:
   778  // types.MetadataReadOnlyVisibility, types.MetadataHiddenVisibility but NOT types.MetadataReadWriteVisibility.
   779  // If the metadata entry is of the GENERAL domain (isSystem=false), visibility is always types.MetadataReadWriteVisibility.
   780  // In terms of typedValues, that must be one of:
   781  // types.MetadataStringValue, types.MetadataNumberValue, types.MetadataDateTimeValue and types.MetadataBooleanValue.
   782  func addMetadata(client *Client, requestUri, name, key, value, typedValue, visibility string, isSystem bool) (Task, error) {
   783  	apiEndpoint := urlParseRequestURI(requestUri)
   784  	newMetadata := &types.MetadataValue{
   785  		Xmlns: types.XMLNamespaceVCloud,
   786  		Xsi:   types.XMLNamespaceXSI,
   787  		TypedValue: &types.MetadataTypedValue{
   788  			XsiType: typedValue,
   789  			Value:   value,
   790  		},
   791  		Domain: &types.MetadataDomainTag{
   792  			Visibility: visibility,
   793  			Domain:     "SYSTEM",
   794  		},
   795  	}
   796  
   797  	if isSystem {
   798  		apiEndpoint.Path += "/metadata/SYSTEM/" + key
   799  	} else {
   800  		apiEndpoint.Path += "/metadata/" + key
   801  		newMetadata.Domain.Domain = "GENERAL"
   802  		if visibility != types.MetadataReadWriteVisibility {
   803  			newMetadata.Domain.Visibility = types.MetadataReadWriteVisibility
   804  		}
   805  	}
   806  
   807  	_, err := filterSingleXmlMetadataEntry(key, requestUri, name, newMetadata, client.IgnoredMetadata)
   808  	if err != nil {
   809  		return Task{}, err
   810  	}
   811  
   812  	domain := newMetadata.Domain.Visibility
   813  	task, err := client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut, types.MimeMetaDataValue, "error adding metadata: %s", newMetadata)
   814  
   815  	// Workaround for ugly error returned by VCD: "API Error: 500: [ <uuid> ] visibility"
   816  	if err != nil && strings.HasSuffix(err.Error(), "visibility") {
   817  		err = fmt.Errorf("error adding metadata with key %s: visibility cannot be %s when domain is %s: %s", key, visibility, domain, err)
   818  	}
   819  	return task, err
   820  }
   821  
   822  // addMetadataAndWait adds metadata to an entity and waits for the task completion.
   823  // The function supports passing a value that requires a typed value that must be one of:
   824  // types.MetadataStringValue, types.MetadataNumberValue, types.MetadataDateTimeValue and types.MetadataBooleanValue.
   825  // Visibility also needs to be one of: types.MetadataReadOnlyVisibility, types.MetadataHiddenVisibility or types.MetadataReadWriteVisibility
   826  func addMetadataAndWait(client *Client, requestUri, name, key, value, typedValue, visibility string, isSystem bool) error {
   827  	task, err := addMetadata(client, requestUri, name, key, value, typedValue, visibility, isSystem)
   828  	if err != nil {
   829  		return err
   830  	}
   831  
   832  	return task.WaitTaskCompletion()
   833  }
   834  
   835  // mergeAllMetadata updates the metadata values that are already present in VCD and creates the ones not present.
   836  // The input metadata map has a "metadata key"->"metadata value" relation.
   837  // If the operation is successful, it returns the created task.
   838  func mergeAllMetadata(client *Client, requestUri, name string, metadata map[string]types.MetadataValue) (Task, error) {
   839  	var metadataToMerge []*types.MetadataEntry
   840  	for key, value := range metadata {
   841  		metadataToMerge = append(metadataToMerge, &types.MetadataEntry{
   842  			Xmlns:      types.XMLNamespaceVCloud,
   843  			Xsi:        types.XMLNamespaceXSI,
   844  			Key:        key,
   845  			TypedValue: value.TypedValue,
   846  			Domain:     value.Domain,
   847  		})
   848  	}
   849  
   850  	newMetadata := &types.Metadata{
   851  		Xmlns:         types.XMLNamespaceVCloud,
   852  		Xsi:           types.XMLNamespaceXSI,
   853  		MetadataEntry: metadataToMerge,
   854  	}
   855  
   856  	apiEndpoint := urlParseRequestURI(requestUri)
   857  	apiEndpoint.Path += "/metadata"
   858  
   859  	filteredMetadata, err := filterXmlMetadata(newMetadata, requestUri, name, client.IgnoredMetadata)
   860  	if err != nil {
   861  		return Task{}, err
   862  	}
   863  	if len(filteredMetadata.MetadataEntry) == 0 {
   864  		return Task{}, fmt.Errorf("after filtering metadata, there is no metadata to merge")
   865  	}
   866  
   867  	return client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, types.MimeMetaData, "error merging metadata: %s", filteredMetadata)
   868  }
   869  
   870  // mergeAllMetadata updates the metadata values that are already present in VCD and creates the ones not present.
   871  // The input metadata map has a "metadata key"->"metadata value" relation.
   872  // This function waits until merge finishes.
   873  func mergeMetadataAndWait(client *Client, requestUri, name string, metadata map[string]types.MetadataValue) error {
   874  	task, err := mergeAllMetadata(client, requestUri, name, metadata)
   875  	if err != nil {
   876  		return err
   877  	}
   878  
   879  	return task.WaitTaskCompletion()
   880  }
   881  
   882  // deleteMetadata deletes metadata associated to the input key from an entity referenced by its URI, then returns the
   883  // task.
   884  func deleteMetadata(client *Client, requestUri, name, key string, isSystem bool) (Task, error) {
   885  	apiEndpoint := urlParseRequestURI(requestUri)
   886  	if isSystem {
   887  		apiEndpoint.Path += "/metadata/SYSTEM/" + key
   888  	} else {
   889  		apiEndpoint.Path += "/metadata/" + key
   890  	}
   891  
   892  	err := filterMetadataToDelete(client, key, requestUri, name, isSystem, client.IgnoredMetadata)
   893  	if err != nil {
   894  		return Task{}, err
   895  	}
   896  
   897  	return client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodDelete, "", "error deleting metadata: %s", nil)
   898  }
   899  
   900  // deleteMetadata deletes metadata associated to the input key from an entity referenced by its URI.
   901  func deleteMetadataAndWait(client *Client, requestUri, name, key string, isSystem bool) error {
   902  	task, err := deleteMetadata(client, requestUri, name, key, isSystem)
   903  	if err != nil {
   904  		return err
   905  	}
   906  
   907  	return task.WaitTaskCompletion()
   908  }
   909  
   910  // IgnoredMetadata is a structure that defines the metadata entries that should be ignored by the VCD Client.
   911  // The filtering works in such a way that all the non-nil pointers in an instance of this struct are evaluated with
   912  // a logical AND.
   913  // For example, ignoredMetadata.ObjectType = "org", ignoredMetadata.ObjectName = "foo" will ignore all metadata from
   914  // Organizations whose name is "foo", with any key and any value.
   915  // Note: This struct is only used by metadata_v2.go methods.
   916  // Note 2: Filtering by ObjectName is not possible in the "ByHref" methods from VCDClient.
   917  type IgnoredMetadata struct {
   918  	ObjectType *string        // Type of the object that has the metadata as defined in the API documentation https://developer.vmware.com/apis/1601/vmware-cloud-director, for example "catalog", "disk", "org"...
   919  	ObjectName *string        // Name of the object
   920  	KeyRegex   *regexp.Regexp // A regular expression to filter out metadata keys
   921  	ValueRegex *regexp.Regexp // A regular expression to filter out metadata values
   922  }
   923  
   924  func (im IgnoredMetadata) String() string {
   925  	objectType := "<nil>"
   926  	if im.ObjectType != nil {
   927  		objectType = *im.ObjectType
   928  	}
   929  
   930  	objectName := "<nil>"
   931  	if im.ObjectName != nil {
   932  		objectName = *im.ObjectName
   933  	}
   934  
   935  	return fmt.Sprintf("IgnoredMetadata(ObjectType=%v, ObjectName=%v, KeyRegex=%v, ValueRegex=%v)", objectType, objectName, im.KeyRegex, im.ValueRegex)
   936  }
   937  
   938  // normalisedMetadata is an auxiliary type that allows to transform XML and OpenAPI metadata into a common structure
   939  // for operations that are executed the same way in both flavors.
   940  type normalisedMetadata struct {
   941  	ObjectType string
   942  	ObjectName string
   943  	Key        string
   944  	Value      string
   945  }
   946  
   947  // normaliseXmlMetadata transforms XML metadata into a normalised structure
   948  func normaliseXmlMetadata(key, href, objectName string, metadataEntry *types.MetadataValue) (*normalisedMetadata, error) {
   949  	objectType, err := getMetadataObjectTypeFromHref(href)
   950  	if err != nil {
   951  		return nil, err
   952  	}
   953  
   954  	return &normalisedMetadata{
   955  		ObjectType: objectType,
   956  		ObjectName: objectName,
   957  		Key:        key,
   958  		Value:      metadataEntry.TypedValue.Value,
   959  	}, nil
   960  }
   961  
   962  // filterXmlMetadata filters all metadata entries, given a slice of metadata that needs to be ignored. It doesn't
   963  // alter the input metadata, but returns a copy of the filtered metadata.
   964  func filterXmlMetadata(allMetadata *types.Metadata, href, objectName string, metadataToIgnore []IgnoredMetadata) (*types.Metadata, error) {
   965  	if len(metadataToIgnore) == 0 {
   966  		return allMetadata, nil
   967  	}
   968  
   969  	result := &types.Metadata{
   970  		XMLName:       allMetadata.XMLName,
   971  		Xmlns:         allMetadata.Xmlns,
   972  		HREF:          allMetadata.HREF,
   973  		Type:          allMetadata.Type,
   974  		Xsi:           allMetadata.Xsi,
   975  		Link:          allMetadata.Link,
   976  		MetadataEntry: nil,
   977  	}
   978  
   979  	var filteredMetadata []*types.MetadataEntry
   980  	for _, originalEntry := range allMetadata.MetadataEntry {
   981  		_, err := filterSingleXmlMetadataEntry(originalEntry.Key, href, objectName, &types.MetadataValue{Domain: originalEntry.Domain, TypedValue: originalEntry.TypedValue}, metadataToIgnore)
   982  		if err != nil {
   983  			if strings.Contains(err.Error(), "ignored") {
   984  				continue
   985  			}
   986  			return nil, err
   987  		}
   988  		filteredMetadata = append(filteredMetadata, originalEntry)
   989  	}
   990  	result.MetadataEntry = filteredMetadata
   991  	return result, nil
   992  }
   993  
   994  func filterSingleXmlMetadataEntry(key, href, objectName string, metadataEntry *types.MetadataValue, metadataToIgnore []IgnoredMetadata) (*types.MetadataValue, error) {
   995  	normalisedEntry, err := normaliseXmlMetadata(key, href, objectName, metadataEntry)
   996  	if err != nil {
   997  		return nil, err
   998  	}
   999  	isFiltered := filterSingleGenericMetadataEntry(normalisedEntry, metadataToIgnore)
  1000  	if isFiltered {
  1001  		return nil, fmt.Errorf("the metadata entry with key '%s' and value '%v' is being ignored", key, metadataEntry.TypedValue.Value)
  1002  	}
  1003  	return metadataEntry, nil
  1004  }
  1005  
  1006  // filterSingleGenericMetadataEntry filters a single metadata entry given a slice of metadata that needs to be ignored. It doesn't
  1007  // alter the input metadata, but returns a bool that indicates whether the entry should be ignored or not.
  1008  func filterSingleGenericMetadataEntry(normalisedMetadataEntry *normalisedMetadata, metadataToIgnore []IgnoredMetadata) bool {
  1009  	if len(metadataToIgnore) == 0 {
  1010  		return false
  1011  	}
  1012  
  1013  	for _, entryToIgnore := range metadataToIgnore {
  1014  		if entryToIgnore.ObjectType == nil && entryToIgnore.ObjectName == nil && entryToIgnore.KeyRegex == nil && entryToIgnore.ValueRegex == nil {
  1015  			continue
  1016  		}
  1017  		util.Logger.Printf("[DEBUG] Comparing metadata with key '%s' with ignored metadata filter '%s'", normalisedMetadataEntry.Key, entryToIgnore)
  1018  		// We apply an optimistic approach here to simplify the conditions, so the metadata entry will always be ignored unless the filters
  1019  		// tell otherwise, that is, if they are nil (not all of them as per the condition above), if they're empty or if they don't match.
  1020  		// All the filtering options (type, name, keyRegex and valueRegex) must compute to true for the metadata to be ignored.
  1021  		if (entryToIgnore.ObjectType == nil || strings.TrimSpace(*entryToIgnore.ObjectType) == "" || *entryToIgnore.ObjectType == normalisedMetadataEntry.ObjectType) &&
  1022  			(entryToIgnore.ObjectName == nil || strings.TrimSpace(*entryToIgnore.ObjectName) == "" || strings.TrimSpace(normalisedMetadataEntry.ObjectName) == "" || *entryToIgnore.ObjectName == normalisedMetadataEntry.ObjectName) &&
  1023  			(entryToIgnore.KeyRegex == nil || entryToIgnore.KeyRegex.MatchString(normalisedMetadataEntry.Key)) &&
  1024  			(entryToIgnore.ValueRegex == nil || entryToIgnore.ValueRegex.MatchString(normalisedMetadataEntry.Value)) {
  1025  			util.Logger.Printf("[DEBUG] the metadata entry with key '%s' and value '%v' is being ignored", normalisedMetadataEntry.ObjectType, normalisedMetadataEntry.Value)
  1026  			return true
  1027  		}
  1028  	}
  1029  	return false
  1030  }
  1031  
  1032  // filterMetadataToDelete filters a metadata entry that is going to be deleted, given a slice of metadata that needs to be ignored.
  1033  func filterMetadataToDelete(client *Client, key, href, objectName string, isSystem bool, metadataToIgnore []IgnoredMetadata) error {
  1034  	if len(metadataToIgnore) == 0 {
  1035  		return nil
  1036  	}
  1037  
  1038  	objectType, err := getMetadataObjectTypeFromHref(href)
  1039  	if err != nil {
  1040  		return err
  1041  	}
  1042  	for _, entryToIgnore := range metadataToIgnore {
  1043  		if entryToIgnore.ObjectType == nil && entryToIgnore.ObjectName == nil && entryToIgnore.KeyRegex == nil && entryToIgnore.ValueRegex == nil {
  1044  			continue
  1045  		}
  1046  
  1047  		if (entryToIgnore.ObjectType == nil || strings.TrimSpace(*entryToIgnore.ObjectType) == "" || *entryToIgnore.ObjectType == objectType) &&
  1048  			(entryToIgnore.ObjectName == nil || strings.TrimSpace(*entryToIgnore.ObjectName) == "" || strings.TrimSpace(objectName) == "" || *entryToIgnore.ObjectName == objectName) &&
  1049  			(entryToIgnore.KeyRegex == nil || entryToIgnore.KeyRegex.MatchString(key)) {
  1050  
  1051  			// Entering here means that it is a good candidate to be ignored, but we need to know the metadata value
  1052  			// as we may be filtering by value
  1053  			ignore := true
  1054  			if entryToIgnore.ValueRegex != nil {
  1055  				metadataEntry, err := getMetadataByKey(client, href, objectName, key, isSystem)
  1056  				if err != nil {
  1057  					return err
  1058  				}
  1059  				if !entryToIgnore.ValueRegex.MatchString(metadataEntry.TypedValue.Value) {
  1060  					ignore = false
  1061  				}
  1062  			}
  1063  
  1064  			if ignore {
  1065  				util.Logger.Printf("[DEBUG] can't delete metadata entry %s as it is ignored", key)
  1066  				return fmt.Errorf("can't delete metadata entry %s as it is ignored", key)
  1067  			}
  1068  			return nil
  1069  		}
  1070  	}
  1071  	return nil
  1072  
  1073  }
  1074  
  1075  // getMetadataObjectTypeFromHref returns the type of the object referenced by the input HREF.
  1076  // For example, "https://atl1-vcd-static-130-117.eng.vmware.com/api/admin/org/11582a00-16bb-4916-a42f-2d5e453ccf36"
  1077  // will return "org".
  1078  func getMetadataObjectTypeFromHref(href string) (string, error) {
  1079  	splitHref := strings.Split(href, "/")
  1080  	if len(splitHref) < 2 {
  1081  		return "", fmt.Errorf("could not find any object type in the provided HREF '%s'", href)
  1082  	}
  1083  	return splitHref[len(splitHref)-2], nil
  1084  }