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

     1  /*
     2   * Copyright 2019 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
     3   */
     4  
     5  package govcd
     6  
     7  import (
     8  	"encoding/xml"
     9  	"errors"
    10  	"fmt"
    11  	"net/http"
    12  	"net/url"
    13  	"regexp"
    14  	"strings"
    15  
    16  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    17  	"github.com/vmware/go-vcloud-director/v2/util"
    18  )
    19  
    20  const labelGlobalDefaultSegmentProfileTemplate = "Global Default Segment Profile Template"
    21  
    22  // Simple structure to pass Edge Gateway creation parameters.
    23  type EdgeGatewayCreation struct {
    24  	ExternalNetworks           []string // List of external networks to be linked to this gateway
    25  	DefaultGateway             string   // Which network should be used as default gateway (empty name = no default gateway)
    26  	OrgName                    string   // parent Org
    27  	VdcName                    string   // parent VDC
    28  	Name                       string   // edge gateway name
    29  	Description                string   // Optional description
    30  	BackingConfiguration       string   // Type of backing configuration (compact, full)
    31  	AdvancedNetworkingEnabled  bool     // enable advanced gateway
    32  	HAEnabled                  bool     // enable HA
    33  	UseDefaultRouteForDNSRelay bool     // True if the default gateway should be used as the DNS relay
    34  	DistributedRoutingEnabled  bool     // If advanced networking enabled, also enable distributed routing
    35  }
    36  
    37  // Creates an Admin Organization based on settings, description, and org name.
    38  // The Organization created will have these settings specified in the
    39  // settings parameter. The settings variable is defined in types.go.
    40  // Method will fail unless user has an admin token.
    41  // API Documentation: https://code.vmware.com/apis/220/vcloud#/doc/doc/operations/POST-CreateOrganization.html
    42  // Organization creation in vCD has two bugs BZ 2177355, BZ 2228936 (fixes are in 9.1.0.3 and 9.5.0.2) which require
    43  // organization settings to be provided as workarounds.
    44  // At least one element among DelayAfterPowerOnSeconds, DeployedVMQuota, StoredVmQuota, UseServerBootSequence, getVdcQuota
    45  // should be set when providing generalOrgSettings.
    46  // If either VAppLeaseSettings or VAppTemplateLeaseSettings is provided then all elements need to have values, otherwise don't provide them at all.
    47  // Overall elements must be in the correct order.
    48  func CreateOrg(vcdClient *VCDClient, name string, fullName string, description string, settings *types.OrgSettings, isEnabled bool) (Task, error) {
    49  	vcomp := &types.AdminOrg{
    50  		Xmlns:       types.XMLNamespaceVCloud,
    51  		Name:        name,
    52  		IsEnabled:   isEnabled,
    53  		FullName:    fullName,
    54  		Description: description,
    55  		OrgSettings: settings,
    56  	}
    57  
    58  	// There is a bug in the settings of CanPublishCatalogs.
    59  	// If UseServerBootSequence is not set, CanPublishCatalogs is always false
    60  	// regardless of the value passed during creation.
    61  	if settings != nil {
    62  		if settings.OrgGeneralSettings != nil {
    63  			settings.OrgGeneralSettings.UseServerBootSequence = true
    64  		}
    65  	}
    66  	orgCreateHREF := vcdClient.Client.VCDHREF
    67  	orgCreateHREF.Path += "/admin/orgs"
    68  
    69  	// Return the task
    70  	return vcdClient.Client.ExecuteTaskRequest(orgCreateHREF.String(), http.MethodPost,
    71  		"application/vnd.vmware.admin.organization+xml", "error instantiating a new Org: %s", vcomp)
    72  
    73  }
    74  
    75  // Returns the UUID part of an entity ID
    76  // From "urn:vcloud:vdc:72fefde7-4fed-45b8-a774-79b72c870325",
    77  // will return "72fefde7-4fed-45b8-a774-79b72c870325"
    78  // From "urn:vcloud:catalog:97384890-180c-4563-b9b7-0dc50a2430b0"
    79  // will return "97384890-180c-4563-b9b7-0dc50a2430b0"
    80  func getBareEntityUuid(entityId string) (string, error) {
    81  	// Regular expression to match an ID:
    82  	//     3 strings (alphanumeric + "-") separated by a colon (:)
    83  	//     1 group of 8 hexadecimal digits
    84  	//     3 groups of 4 hexadecimal digits
    85  	//     1 group of 12 hexadecimal digits
    86  	reGetID := regexp.MustCompile(`^[\w-]+:[\w-]+:[\w-]+:([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$`)
    87  	matchList := reGetID.FindAllStringSubmatch(entityId, -1)
    88  
    89  	// matchList has the format
    90  	// [][]string{[]string{"TOTAL MATCHED STRING", "CAPTURED TEXT"}}
    91  	// such as
    92  	// [][]string{[]string{"urn:vcloud:catalog:97384890-180c-4563-b9b7-0dc50a2430b0", "97384890-180c-4563-b9b7-0dc50a2430b0"}}
    93  	if len(matchList) == 0 || len(matchList[0]) < 2 {
    94  		return "", fmt.Errorf("error extracting ID from '%s'", entityId)
    95  	}
    96  	return matchList[0][1], nil
    97  }
    98  
    99  // CreateEdgeGatewayAsync creates an edge gateway using a simplified configuration structure
   100  // https://code.vmware.com/apis/442/vcloud-director/doc/doc/operations/POST-CreateEdgeGateway.html
   101  //
   102  // Note. This function does not allow to pick exact subnet in external network to use for edge
   103  // gateway. It will pick first one instead.
   104  func CreateEdgeGatewayAsync(vcdClient *VCDClient, egwc EdgeGatewayCreation) (Task, error) {
   105  
   106  	distributed := egwc.DistributedRoutingEnabled
   107  	if !egwc.AdvancedNetworkingEnabled {
   108  		distributed = false
   109  	}
   110  	// This is the main configuration structure
   111  	egwConfiguration := &types.EdgeGateway{
   112  		Xmlns:       types.XMLNamespaceVCloud,
   113  		Name:        egwc.Name,
   114  		Description: egwc.Description,
   115  		Configuration: &types.GatewayConfiguration{
   116  			UseDefaultRouteForDNSRelay: &egwc.UseDefaultRouteForDNSRelay,
   117  			HaEnabled:                  &egwc.HAEnabled,
   118  			GatewayBackingConfig:       egwc.BackingConfiguration,
   119  			AdvancedNetworkingEnabled:  &egwc.AdvancedNetworkingEnabled,
   120  			DistributedRoutingEnabled:  &distributed,
   121  			GatewayInterfaces: &types.GatewayInterfaces{
   122  				GatewayInterface: []*types.GatewayInterface{},
   123  			},
   124  			EdgeGatewayServiceConfiguration: &types.GatewayFeatures{},
   125  		},
   126  	}
   127  
   128  	if len(egwc.ExternalNetworks) == 0 {
   129  		return Task{}, fmt.Errorf("no external networks provided. At least one is needed")
   130  	}
   131  
   132  	// If the user has indicated a default gateway, we make sure that it matches
   133  	// a name in the list of external networks
   134  	if egwc.DefaultGateway != "" {
   135  		defaultGatewayFound := false
   136  		for _, name := range egwc.ExternalNetworks {
   137  			if egwc.DefaultGateway == name {
   138  				defaultGatewayFound = true
   139  			}
   140  		}
   141  		if !defaultGatewayFound {
   142  			return Task{}, fmt.Errorf("default gateway (%s) selected, but its name is not among the external networks (%v)", egwc.DefaultGateway, egwc.ExternalNetworks)
   143  		}
   144  	}
   145  	// Add external networks inside the configuration structure
   146  	for _, extNetName := range egwc.ExternalNetworks {
   147  		extNet, err := vcdClient.GetExternalNetworkByName(extNetName)
   148  		if err != nil {
   149  			return Task{}, err
   150  		}
   151  
   152  		// Populate the subnet participation only if default gateway was set
   153  		var subnetParticipation *types.SubnetParticipation
   154  		if egwc.DefaultGateway != "" && extNet.ExternalNetwork.Name == egwc.DefaultGateway {
   155  			for _, net := range extNet.ExternalNetwork.Configuration.IPScopes.IPScope {
   156  				if net.IsEnabled {
   157  					subnetParticipation = &types.SubnetParticipation{
   158  						Gateway: net.Gateway,
   159  						Netmask: net.Netmask,
   160  					}
   161  					break
   162  				}
   163  			}
   164  		}
   165  		networkConf := &types.GatewayInterface{
   166  			Name:          extNet.ExternalNetwork.Name,
   167  			DisplayName:   extNet.ExternalNetwork.Name,
   168  			InterfaceType: "uplink",
   169  			Network: &types.Reference{
   170  				HREF: extNet.ExternalNetwork.HREF,
   171  				ID:   extNet.ExternalNetwork.ID,
   172  				Type: "application/vnd.vmware.admin.network+xml",
   173  				Name: extNet.ExternalNetwork.Name,
   174  			},
   175  			UseForDefaultRoute:  egwc.DefaultGateway == extNet.ExternalNetwork.Name,
   176  			SubnetParticipation: []*types.SubnetParticipation{subnetParticipation},
   177  		}
   178  
   179  		egwConfiguration.Configuration.GatewayInterfaces.GatewayInterface =
   180  			append(egwConfiguration.Configuration.GatewayInterfaces.GatewayInterface, networkConf)
   181  	}
   182  
   183  	// Once the configuration structure has been filled using the simplified data, we delegate
   184  	// the edge gateway creation to the main configuration function.
   185  	return CreateAndConfigureEdgeGatewayAsync(vcdClient, egwc.OrgName, egwc.VdcName, egwc.Name, egwConfiguration)
   186  }
   187  
   188  // CreateAndConfigureEdgeGatewayAsync creates an edge gateway using a full configuration structure
   189  func CreateAndConfigureEdgeGatewayAsync(vcdClient *VCDClient, orgName, vdcName, egwName string, egwConfiguration *types.EdgeGateway) (Task, error) {
   190  
   191  	if egwConfiguration.Name != egwName {
   192  		return Task{}, fmt.Errorf("name mismatch: '%s' used as parameter but '%s' in the configuration structure", egwName, egwConfiguration.Name)
   193  	}
   194  
   195  	egwConfiguration.Xmlns = types.XMLNamespaceVCloud
   196  
   197  	adminOrg, err := vcdClient.GetAdminOrgByName(orgName)
   198  	if err != nil {
   199  		return Task{}, err
   200  	}
   201  	vdc, err := adminOrg.GetVDCByName(vdcName, false)
   202  	if err != nil {
   203  		return Task{}, err
   204  	}
   205  
   206  	egwCreateHREF := vcdClient.Client.VCDHREF
   207  
   208  	vdcId, err := getBareEntityUuid(vdc.Vdc.ID)
   209  	if err != nil {
   210  		return Task{}, fmt.Errorf("error retrieving ID from Vdc %s: %s", vdcName, err)
   211  	}
   212  	if vdcId == "" {
   213  		return Task{}, fmt.Errorf("error retrieving ID from Vdc %s - empty ID returned", vdcName)
   214  	}
   215  	egwCreateHREF.Path += fmt.Sprintf("/admin/vdc/%s/edgeGateways", vdcId)
   216  
   217  	// The first task is the creation task. It is quick, and does only create the vCD entity,
   218  	// but not yet deploy the underlying VM
   219  	creationTask, err := vcdClient.Client.ExecuteTaskRequest(egwCreateHREF.String(), http.MethodPost,
   220  		"application/vnd.vmware.admin.edgeGateway+xml", "error instantiating a new Edge Gateway: %s", egwConfiguration)
   221  
   222  	if err != nil {
   223  		return Task{}, err
   224  	}
   225  
   226  	err = creationTask.WaitTaskCompletion()
   227  
   228  	if err != nil {
   229  		return Task{}, err
   230  	}
   231  
   232  	// After creation, there is a build task that supervises the gateway deployment
   233  	for _, innerTask := range creationTask.Task.Tasks.Task {
   234  		if innerTask.OperationName == "networkEdgeGatewayCreate" {
   235  			deployTask := Task{
   236  				Task:   innerTask,
   237  				client: &vcdClient.Client,
   238  			}
   239  			return deployTask, nil
   240  		}
   241  	}
   242  	return Task{}, fmt.Errorf("no deployment task found for edge gateway %s - The edge gateway might have been created, but not deployed properly", egwName)
   243  }
   244  
   245  // Private convenience function used by CreateAndConfigureEdgeGateway and CreateEdgeGateway to
   246  // process the task and return the object that was created.
   247  // It should not be invoked directly.
   248  func createEdgeGateway(vcdClient *VCDClient, egwc EdgeGatewayCreation, egwConfiguration *types.EdgeGateway) (EdgeGateway, error) {
   249  	var task Task
   250  	var err error
   251  	if egwConfiguration != nil {
   252  		task, err = CreateAndConfigureEdgeGatewayAsync(vcdClient, egwc.OrgName, egwc.VdcName, egwc.Name, egwConfiguration)
   253  	} else {
   254  		task, err = CreateEdgeGatewayAsync(vcdClient, egwc)
   255  	}
   256  
   257  	if err != nil {
   258  		return EdgeGateway{}, err
   259  	}
   260  	err = task.WaitTaskCompletion()
   261  	if err != nil {
   262  		return EdgeGateway{}, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err))
   263  	}
   264  
   265  	// The edge gateway is created. Now we retrieve it from the server
   266  	org, err := vcdClient.GetAdminOrgByName(egwc.OrgName)
   267  	if err != nil {
   268  		return EdgeGateway{}, err
   269  	}
   270  	vdc, err := org.GetVDCByName(egwc.VdcName, false)
   271  	if err != nil {
   272  		return EdgeGateway{}, err
   273  	}
   274  	egw, err := vdc.GetEdgeGatewayByName(egwc.Name, false)
   275  	if err != nil {
   276  		return EdgeGateway{}, err
   277  	}
   278  	return *egw, nil
   279  }
   280  
   281  // CreateAndConfigureEdgeGateway creates an edge gateway using a full configuration structure
   282  func CreateAndConfigureEdgeGateway(vcdClient *VCDClient, orgName, vdcName, egwName string, egwConfiguration *types.EdgeGateway) (EdgeGateway, error) {
   283  	return createEdgeGateway(vcdClient, EdgeGatewayCreation{OrgName: orgName, VdcName: vdcName, Name: egwName}, egwConfiguration)
   284  }
   285  
   286  // CreateEdgeGateway creates an edge gateway using a simplified configuration structure
   287  func CreateEdgeGateway(vcdClient *VCDClient, egwc EdgeGatewayCreation) (EdgeGateway, error) {
   288  	return createEdgeGateway(vcdClient, egwc, nil)
   289  }
   290  
   291  func getOrgByHref(vcdClient *Client, href string) (*Org, error) {
   292  	org := NewOrg(vcdClient)
   293  
   294  	_, err := vcdClient.ExecuteRequest(href, http.MethodGet,
   295  		"", "error retrieving org list: %s", nil, org.Org)
   296  	if err != nil {
   297  		return nil, err
   298  	}
   299  
   300  	tenantContext, err := org.getTenantContext()
   301  	if err != nil {
   302  		return nil, err
   303  	}
   304  	org.TenantContext = tenantContext
   305  
   306  	return org, nil
   307  }
   308  
   309  func getAdminOrgByHref(vcdClient *Client, href string) (*AdminOrg, error) {
   310  	adminOrg := NewAdminOrg(vcdClient)
   311  
   312  	_, err := vcdClient.ExecuteRequest(href, http.MethodGet,
   313  		"", "error retrieving org list: %s", nil, adminOrg.AdminOrg)
   314  	if err != nil {
   315  		return nil, err
   316  	}
   317  
   318  	tenantContext, err := adminOrg.getTenantContext()
   319  	if err != nil {
   320  		return nil, err
   321  	}
   322  	adminOrg.TenantContext = tenantContext
   323  
   324  	return adminOrg, nil
   325  }
   326  
   327  // If user specifies a valid organization name, then this returns a
   328  // organization object. If no valid org is found, it returns an empty
   329  // org and no error. Otherwise it returns an error and an empty
   330  // Org object
   331  // Deprecated: Use vcdClient.GetOrgByName instead
   332  func GetOrgByName(vcdClient *VCDClient, orgName string) (Org, error) {
   333  	orgUrl, err := getOrgHREF(vcdClient, orgName)
   334  	if err != nil {
   335  		return Org{}, fmt.Errorf("organization '%s' fetch failed: %s", orgName, err)
   336  	}
   337  	org := NewOrg(&vcdClient.Client)
   338  
   339  	_, err = vcdClient.Client.ExecuteRequest(orgUrl, http.MethodGet,
   340  		"", "error retrieving org list: %s", nil, org.Org)
   341  	if err != nil {
   342  		return Org{}, err
   343  	}
   344  
   345  	return *org, nil
   346  }
   347  
   348  // If user specifies valid organization name,
   349  // then this returns an admin organization object.
   350  // If no valid org is found, it returns an empty
   351  // org and no error. Otherwise returns an empty AdminOrg
   352  // and an error.
   353  // API Documentation: https://code.vmware.com/apis/220/vcloud#/doc/doc/operations/GET-Organization-AdminView.html
   354  // Deprecated: Use vcdClient.GetAdminOrgByName instead
   355  func GetAdminOrgByName(vcdClient *VCDClient, orgName string) (AdminOrg, error) {
   356  	orgUrl, err := getOrgHREF(vcdClient, orgName)
   357  	if err != nil {
   358  		return AdminOrg{}, err
   359  	}
   360  	orgHREF := vcdClient.Client.VCDHREF
   361  	orgHREF.Path += "/admin/org/" + strings.Split(orgUrl, "/api/org/")[1]
   362  
   363  	org := NewAdminOrg(&vcdClient.Client)
   364  
   365  	_, err = vcdClient.Client.ExecuteRequest(orgHREF.String(), http.MethodGet,
   366  		"", "error retrieving org: %s", nil, org.AdminOrg)
   367  	if err != nil {
   368  		return AdminOrg{}, err
   369  	}
   370  
   371  	return *org, nil
   372  }
   373  
   374  // Returns the HREF of the org with the name orgName
   375  func getOrgHREF(vcdClient *VCDClient, orgName string) (string, error) {
   376  	orgListHREF := vcdClient.Client.VCDHREF
   377  	orgListHREF.Path += "/org"
   378  
   379  	orgList := new(types.OrgList)
   380  
   381  	_, err := vcdClient.Client.ExecuteRequest(orgListHREF.String(), http.MethodGet,
   382  		"", "error retrieving org list: %s", nil, orgList)
   383  	if err != nil {
   384  		return "", err
   385  	}
   386  
   387  	// Look for orgName within OrgList
   388  	for _, org := range orgList.Org {
   389  		if org.Name == orgName {
   390  			return org.HREF, nil
   391  		}
   392  	}
   393  	return "", fmt.Errorf("couldn't find org with name: %s. Please check Org name as it is case sensitive", orgName)
   394  }
   395  
   396  // Returns the HREF of the org from the org ID
   397  func getOrgHREFById(vcdClient *VCDClient, orgId string) (string, error) {
   398  	orgListHREF := vcdClient.Client.VCDHREF
   399  	orgListHREF.Path += "/org"
   400  
   401  	orgList := new(types.OrgList)
   402  
   403  	_, err := vcdClient.Client.ExecuteRequest(orgListHREF.String(), http.MethodGet,
   404  		"", "error retrieving org list: %s", nil, orgList)
   405  	if err != nil {
   406  		return "", err
   407  	}
   408  
   409  	orgUuid, err := getBareEntityUuid(orgId)
   410  	if err != nil {
   411  		return "", err
   412  	}
   413  	// Look for org UUID within OrgList
   414  	for _, org := range orgList.Org {
   415  		// ID in orgList is usually empty. We extract the UUID from HREF to make the comparison
   416  		uuidFromHref, err := GetUuidFromHref(org.HREF, true)
   417  		if err != nil {
   418  			return "", err
   419  		}
   420  		if uuidFromHref == orgUuid {
   421  			return org.HREF, nil
   422  		}
   423  	}
   424  	return "", fmt.Errorf("couldn't find org with ID: %s", orgId)
   425  }
   426  
   427  // Find a list of Virtual Centers matching the filter parameter.
   428  // Filter constructing guide: https://pubs.vmware.com/vcloud-api-1-5/wwhelp/wwhimpl/js/html/wwhelp.htm#href=api_prog/GUID-CDF04296-5EB5-47E1-9BEC-228837C584CE.html
   429  // Possible parameters are any attribute from QueryResultVirtualCenterRecordType struct
   430  // E.g. filter could look like: name==vC1
   431  func QueryVirtualCenters(vcdClient *VCDClient, filter string) ([]*types.QueryResultVirtualCenterRecordType, error) {
   432  	results, err := vcdClient.QueryWithNotEncodedParams(nil, map[string]string{
   433  		"type":         "virtualCenter",
   434  		"filter":       filter,
   435  		"filterEncode": "true",
   436  	})
   437  	if err != nil {
   438  		return nil, err
   439  	}
   440  
   441  	return results.Results.VirtualCenterRecord, nil
   442  }
   443  
   444  // Find a Network port group by name
   445  func QueryNetworkPortGroup(vcdCli *VCDClient, name string) ([]*types.PortGroupRecordType, error) {
   446  	return QueryPortGroups(vcdCli, fmt.Sprintf("name==%s;portgroupType==%s", url.QueryEscape(name), "NETWORK"))
   447  }
   448  
   449  // Find a Distributed port group by name
   450  func QueryDistributedPortGroup(vcdCli *VCDClient, name string) ([]*types.PortGroupRecordType, error) {
   451  	return QueryPortGroups(vcdCli, fmt.Sprintf("name==%s;portgroupType==%s", url.QueryEscape(name), "DV_PORTGROUP"))
   452  }
   453  
   454  // Find a list of Port groups matching the filter parameter.
   455  func QueryPortGroups(vcdCli *VCDClient, filter string) ([]*types.PortGroupRecordType, error) {
   456  	results, err := vcdCli.QueryWithNotEncodedParams(nil, map[string]string{
   457  		"type":          "portgroup",
   458  		"filter":        filter,
   459  		"filterEncoded": "true",
   460  	})
   461  	if err != nil {
   462  		return nil, err
   463  	}
   464  
   465  	return results.Results.PortGroupRecord, nil
   466  }
   467  
   468  // GetExternalNetwork returns an ExternalNetwork reference if the network name matches an existing one.
   469  // If no valid external network is found, it returns an empty ExternalNetwork reference and an error
   470  // Deprecated: use vcdClient.GetExternalNetworkByName instead
   471  func GetExternalNetwork(vcdClient *VCDClient, networkName string) (*ExternalNetwork, error) {
   472  
   473  	if !vcdClient.Client.IsSysAdmin {
   474  		return &ExternalNetwork{}, fmt.Errorf("functionality requires System Administrator privileges")
   475  	}
   476  
   477  	extNetworkHREF, err := getExternalNetworkHref(&vcdClient.Client)
   478  	if err != nil {
   479  		return &ExternalNetwork{}, err
   480  	}
   481  
   482  	extNetworkRefs := &types.ExternalNetworkReferences{}
   483  	_, err = vcdClient.Client.ExecuteRequest(extNetworkHREF, http.MethodGet,
   484  		types.MimeNetworkConnectionSection, "error retrieving external networks: %s", nil, extNetworkRefs)
   485  	if err != nil {
   486  		return &ExternalNetwork{}, err
   487  	}
   488  
   489  	externalNetwork := NewExternalNetwork(&vcdClient.Client)
   490  
   491  	found := false
   492  	for _, netRef := range extNetworkRefs.ExternalNetworkReference {
   493  		if netRef.Name == networkName {
   494  			externalNetwork.ExternalNetwork.HREF = netRef.HREF
   495  			err = externalNetwork.Refresh()
   496  			found = true
   497  			if err != nil {
   498  				return &ExternalNetwork{}, err
   499  			}
   500  		}
   501  	}
   502  
   503  	if found {
   504  		return externalNetwork, nil
   505  	}
   506  	return externalNetwork, fmt.Errorf("could not find external network named %s", networkName)
   507  
   508  }
   509  
   510  // GetExternalNetworks returns a list of available external networks
   511  func (vcdClient *VCDClient) GetExternalNetworks() (*types.ExternalNetworkReferences, error) {
   512  
   513  	if !vcdClient.Client.IsSysAdmin {
   514  		return nil, fmt.Errorf("functionality requires System Administrator privileges")
   515  	}
   516  
   517  	extNetworkHREF, err := getExternalNetworkHref(&vcdClient.Client)
   518  	if err != nil {
   519  		return nil, err
   520  	}
   521  
   522  	extNetworkRefs := &types.ExternalNetworkReferences{}
   523  	_, err = vcdClient.Client.ExecuteRequest(extNetworkHREF, http.MethodGet,
   524  		types.MimeNetworkConnectionSection, "error retrieving external networks: %s", nil, extNetworkRefs)
   525  	if err != nil {
   526  		return nil, err
   527  	}
   528  
   529  	return extNetworkRefs, nil
   530  }
   531  
   532  // GetExternalNetworkByName returns an ExternalNetwork reference if the network name matches an existing one.
   533  // If no valid external network is found, it returns a nil ExternalNetwork reference and an error
   534  func (vcdClient *VCDClient) GetExternalNetworkByName(networkName string) (*ExternalNetwork, error) {
   535  
   536  	extNetworkRefs, err := vcdClient.GetExternalNetworks()
   537  
   538  	if err != nil {
   539  		return nil, err
   540  	}
   541  
   542  	externalNetwork := NewExternalNetwork(&vcdClient.Client)
   543  
   544  	for _, netRef := range extNetworkRefs.ExternalNetworkReference {
   545  		if netRef.Name == networkName {
   546  			externalNetwork.ExternalNetwork.HREF = netRef.HREF
   547  			err = externalNetwork.Refresh()
   548  			if err != nil {
   549  				return nil, err
   550  			}
   551  			return externalNetwork, nil
   552  		}
   553  	}
   554  
   555  	return nil, ErrorEntityNotFound
   556  }
   557  
   558  // GetExternalNetworkById returns an ExternalNetwork reference if the network ID matches an existing one.
   559  // If no valid external network is found, it returns a nil ExternalNetwork reference and an error
   560  func (vcdClient *VCDClient) GetExternalNetworkById(id string) (*ExternalNetwork, error) {
   561  
   562  	extNetworkRefs, err := vcdClient.GetExternalNetworks()
   563  
   564  	if err != nil {
   565  		return nil, err
   566  	}
   567  
   568  	externalNetwork := NewExternalNetwork(&vcdClient.Client)
   569  
   570  	for _, netRef := range extNetworkRefs.ExternalNetworkReference {
   571  		// ExternalNetworkReference items don't have ID
   572  		// We compare using the UUID from HREF
   573  		if equalIds(id, "", netRef.HREF) {
   574  			externalNetwork.ExternalNetwork.HREF = netRef.HREF
   575  			err = externalNetwork.Refresh()
   576  			if err != nil {
   577  				return nil, err
   578  			}
   579  			return externalNetwork, nil
   580  		}
   581  	}
   582  
   583  	return nil, ErrorEntityNotFound
   584  }
   585  
   586  // GetExternalNetworkByNameOrId returns an ExternalNetwork reference if either the network name or ID matches an existing one.
   587  // If no valid external network is found, it returns a nil ExternalNetwork reference and an error
   588  func (vcdClient *VCDClient) GetExternalNetworkByNameOrId(identifier string) (*ExternalNetwork, error) {
   589  	getByName := func(name string, refresh bool) (interface{}, error) { return vcdClient.GetExternalNetworkByName(name) }
   590  	getById := func(id string, refresh bool) (interface{}, error) { return vcdClient.GetExternalNetworkById(id) }
   591  	entity, err := getEntityByNameOrId(getByName, getById, identifier, false)
   592  	if entity == nil {
   593  		return nil, err
   594  	}
   595  	return entity.(*ExternalNetwork), err
   596  }
   597  
   598  // CreateExternalNetwork allows create external network and returns Task or error.
   599  // types.ExternalNetwork struct is general and used for various types of networks. But for external network
   600  // fence mode is always isolated, isInherited is false, parentNetwork is empty.
   601  func CreateExternalNetwork(vcdClient *VCDClient, externalNetworkData *types.ExternalNetwork) (Task, error) {
   602  
   603  	if !vcdClient.Client.IsSysAdmin {
   604  		return Task{}, fmt.Errorf("functionality requires System Administrator privileges")
   605  	}
   606  
   607  	err := validateExternalNetwork(externalNetworkData)
   608  	if err != nil {
   609  		return Task{}, err
   610  	}
   611  
   612  	// Type: VimObjectRefType
   613  	// Namespace: http://www.vmware.com/vcloud/extension/v1.5
   614  	// https://vdc-repo.vmware.com/vmwb-repository/dcr-public/7a028e78-bd37-4a6a-8298-9c26c7eeb9aa/09142237-dd46-4dee-8326-e07212fb63a8/doc/doc/types/VimObjectRefsType.html
   615  	// Description: Represents the Managed Object Reference (MoRef) and the type of a vSphere object.
   616  	// Since: 0.9
   617  	type vimObjectRefCreate struct {
   618  		VimServerRef  *types.Reference `xml:"vmext:VimServerRef"`
   619  		MoRef         string           `xml:"vmext:MoRef"`
   620  		VimObjectType string           `xml:"vmext:VimObjectType"`
   621  	}
   622  
   623  	// Type: VimObjectRefsType
   624  	// Namespace: http://www.vmware.com/vcloud/extension/v1.5
   625  	// https://vdc-repo.vmware.com/vmwb-repository/dcr-public/7a028e78-bd37-4a6a-8298-9c26c7eeb9aa/09142237-dd46-4dee-8326-e07212fb63a8/doc/doc/types/VimObjectRefsType.html
   626  	// Description: List of VimObjectRef elements.
   627  	// Since: 0.9
   628  	type vimObjectRefsCreate struct {
   629  		VimObjectRef []*vimObjectRefCreate `xml:"vmext:VimObjectRef"`
   630  	}
   631  
   632  	// Type: VMWExternalNetworkType
   633  	// Namespace: http://www.vmware.com/vcloud/extension/v1.5
   634  	// https://vdc-repo.vmware.com/vmwb-repository/dcr-public/7a028e78-bd37-4a6a-8298-9c26c7eeb9aa/09142237-dd46-4dee-8326-e07212fb63a8/doc/doc/types/VMWExternalNetworkType.html
   635  	// Description: External network type.
   636  	// Since: 1.0
   637  	type externalNetworkCreate struct {
   638  		XMLName          xml.Name                    `xml:"vmext:VMWExternalNetwork"`
   639  		XmlnsVmext       string                      `xml:"xmlns:vmext,attr,omitempty"`
   640  		XmlnsVcloud      string                      `xml:"xmlns:vcloud,attr,omitempty"`
   641  		HREF             string                      `xml:"href,attr,omitempty"`
   642  		Type             string                      `xml:"type,attr,omitempty"`
   643  		ID               string                      `xml:"id,attr,omitempty"`
   644  		OperationKey     string                      `xml:"operationKey,attr,omitempty"`
   645  		Name             string                      `xml:"name,attr"`
   646  		Link             []*types.Link               `xml:"Link,omitempty"`
   647  		Description      string                      `xml:"vcloud:Description,omitempty"`
   648  		Tasks            *types.TasksInProgress      `xml:"Tasks,omitempty"`
   649  		Configuration    *types.NetworkConfiguration `xml:"vcloud:Configuration,omitempty"`
   650  		VimPortGroupRef  *vimObjectRefCreate         `xml:"VimPortGroupRef,omitempty"`
   651  		VimPortGroupRefs *vimObjectRefsCreate        `xml:"vmext:VimPortGroupRefs,omitempty"`
   652  		VCloudExtension  *types.VCloudExtension      `xml:"VCloudExtension,omitempty"`
   653  	}
   654  
   655  	// Specific struct is used as two different name spaces needed for vCD API and return struct has diff name spaces
   656  	externalNetwork := &externalNetworkCreate{}
   657  	externalNetwork.HREF = externalNetworkData.HREF
   658  	externalNetwork.Description = externalNetworkData.Description
   659  	externalNetwork.Name = externalNetworkData.Name
   660  	externalNetwork.Type = externalNetworkData.Type
   661  	externalNetwork.ID = externalNetworkData.ID
   662  	externalNetwork.OperationKey = externalNetworkData.OperationKey
   663  	externalNetwork.Link = externalNetworkData.Link
   664  	externalNetwork.Configuration = externalNetworkData.Configuration
   665  	if externalNetwork.Configuration != nil {
   666  		externalNetwork.Configuration.Xmlns = types.XMLNamespaceVCloud
   667  	}
   668  	externalNetwork.VCloudExtension = externalNetworkData.VCloudExtension
   669  	externalNetwork.XmlnsVmext = types.XMLNamespaceExtension
   670  	externalNetwork.XmlnsVcloud = types.XMLNamespaceVCloud
   671  	externalNetwork.Type = types.MimeExternalNetwork
   672  	if externalNetworkData.VimPortGroupRefs != nil {
   673  		externalNetwork.VimPortGroupRefs = &vimObjectRefsCreate{}
   674  		for _, vimObjRef := range externalNetworkData.VimPortGroupRefs.VimObjectRef {
   675  			externalNetwork.VimPortGroupRefs.VimObjectRef = append(externalNetwork.VimPortGroupRefs.VimObjectRef, &vimObjectRefCreate{
   676  				VimServerRef:  vimObjRef.VimServerRef,
   677  				MoRef:         vimObjRef.MoRef,
   678  				VimObjectType: vimObjRef.VimObjectType,
   679  			})
   680  		}
   681  	}
   682  	if externalNetworkData.VimPortGroupRef != nil {
   683  		externalNetwork.VimPortGroupRef = &vimObjectRefCreate{
   684  			VimServerRef:  externalNetworkData.VimPortGroupRef.VimServerRef,
   685  			MoRef:         externalNetworkData.VimPortGroupRef.MoRef,
   686  			VimObjectType: externalNetworkData.VimPortGroupRef.VimObjectType,
   687  		}
   688  	}
   689  
   690  	externalNetHREF := vcdClient.Client.VCDHREF
   691  	externalNetHREF.Path += "/admin/extension/externalnets"
   692  
   693  	if externalNetwork.Configuration == nil {
   694  		externalNetwork.Configuration = &types.NetworkConfiguration{}
   695  	}
   696  	externalNetwork.Configuration.FenceMode = "isolated"
   697  
   698  	// Return the task
   699  	task, err := vcdClient.Client.ExecuteTaskRequest(externalNetHREF.String(), http.MethodPost,
   700  		types.MimeExternalNetwork, "error instantiating a new ExternalNetwork: %s", externalNetwork)
   701  
   702  	if err != nil {
   703  		return Task{}, err
   704  	}
   705  	if task.Task == nil || task.Task.Tasks == nil || len(task.Task.Tasks.Task) == 0 {
   706  		return Task{}, fmt.Errorf("create external network task wasn't found")
   707  	}
   708  	// Real task in task array
   709  	task.Task = task.Task.Tasks.Task[0]
   710  
   711  	return task, err
   712  }
   713  
   714  func getExtension(client *Client) (*types.Extension, error) {
   715  	extensions := &types.Extension{}
   716  
   717  	extensionHREF := client.VCDHREF
   718  	extensionHREF.Path += "/admin/extension/"
   719  
   720  	_, err := client.ExecuteRequest(extensionHREF.String(), http.MethodGet,
   721  		"", "error retrieving extension: %s", nil, extensions)
   722  
   723  	return extensions, err
   724  }
   725  
   726  // GetStorageProfileById fetches a storage profile using its ID.
   727  func (vcdClient *VCDClient) GetStorageProfileById(id string) (*types.VdcStorageProfile, error) {
   728  	return getStorageProfileById(&vcdClient.Client, id)
   729  }
   730  
   731  // getStorageProfileById fetches a storage profile using its ID.
   732  func getStorageProfileById(client *Client, id string) (*types.VdcStorageProfile, error) {
   733  	storageProfileHref := client.VCDHREF
   734  	storageProfileHref.Path += "/admin/vdcStorageProfile/" + extractUuid(id)
   735  
   736  	vdcStorageProfile := &types.VdcStorageProfile{}
   737  
   738  	_, err := client.ExecuteRequest(storageProfileHref.String(), http.MethodGet, "", "error retrieving storage profile: %s", nil, vdcStorageProfile)
   739  	if err != nil {
   740  		return nil, err
   741  	}
   742  
   743  	return vdcStorageProfile, nil
   744  }
   745  
   746  // GetStorageProfileByHref fetches storage profile using provided HREF.
   747  // Deprecated: use client.GetStorageProfileByHref or vcdClient.GetStorageProfileByHref
   748  func GetStorageProfileByHref(vcdClient *VCDClient, url string) (*types.VdcStorageProfile, error) {
   749  	return vcdClient.Client.GetStorageProfileByHref(url)
   750  }
   751  
   752  // GetStorageProfileByHref fetches a storage profile using its HREF.
   753  func (vcdClient *VCDClient) GetStorageProfileByHref(url string) (*types.VdcStorageProfile, error) {
   754  	return vcdClient.Client.GetStorageProfileByHref(url)
   755  }
   756  
   757  // GetStorageProfileByHref fetches a storage profile using its HREF.
   758  func (client *Client) GetStorageProfileByHref(url string) (*types.VdcStorageProfile, error) {
   759  
   760  	vdcStorageProfile := &types.VdcStorageProfile{}
   761  
   762  	_, err := client.ExecuteRequest(url, http.MethodGet, "", "error retrieving storage profile: %s", nil, vdcStorageProfile)
   763  	if err != nil {
   764  		return nil, err
   765  	}
   766  
   767  	return vdcStorageProfile, nil
   768  }
   769  
   770  // QueryProviderVdcStorageProfileByName finds a provider VDC storage profile by name
   771  // There are four cases:
   772  // 1. [FOUND] The name matches and is unique among all the storage profiles
   773  // 2. [FOUND] The name matches, it is not unique, and it is disambiguated by the provider VDC HREF
   774  // 3. [NOT FOUND] The name matches, is not unique, but no Provider HREF was given: the search will fail
   775  // 4. [NOT FOUND] The name does not match any of the storage profiles
   776  func (vcdClient *VCDClient) QueryProviderVdcStorageProfileByName(name, providerVDCHref string) (*types.QueryResultProviderVdcStorageProfileRecordType, error) {
   777  
   778  	results, err := vcdClient.Client.cumulativeQuery(types.QtProviderVdcStorageProfile, nil, map[string]string{
   779  		"type": types.QtProviderVdcStorageProfile})
   780  	if err != nil {
   781  		return nil, err
   782  	}
   783  
   784  	var recs []*types.QueryResultProviderVdcStorageProfileRecordType
   785  	for _, rec := range results.Results.ProviderVdcStorageProfileRecord {
   786  		if rec.Name == name {
   787  			// Double match: both the name and the provider VDC match: we can return the result
   788  			if providerVDCHref != "" && providerVDCHref == rec.ProviderVdcHREF {
   789  				return rec, nil
   790  			}
   791  			// if there is a name match, but no provider VDC was given, we add to the result, and we will check later.
   792  			if providerVDCHref == "" {
   793  				recs = append(recs, rec)
   794  			}
   795  		}
   796  	}
   797  
   798  	providerVDCMessage := ""
   799  	if providerVDCHref != "" {
   800  		providerVDCMessage = fmt.Sprintf("in provider VDC '%s'", providerVDCHref)
   801  	}
   802  	if len(recs) == 0 {
   803  		return nil, fmt.Errorf("no records found for storage profile '%s' %s", name, providerVDCMessage)
   804  	}
   805  	if len(recs) > 1 {
   806  		return nil, fmt.Errorf("more than 1 record found for storage profile '%s'. Add Provider VDC HREF in the search to disambiguate", name)
   807  	}
   808  	return recs[0], nil
   809  }
   810  
   811  // QueryProviderVdcStorageProfileByName finds a provider VDC storage profile by name
   812  // Deprecated: wrong implementation. Use VCDClient.QueryProviderVdcStorageProfileByName
   813  func QueryProviderVdcStorageProfileByName(vcdCli *VCDClient, name string) ([]*types.QueryResultProviderVdcStorageProfileRecordType, error) {
   814  	results, err := vcdCli.QueryWithNotEncodedParams(nil, map[string]string{
   815  		"type":          "providerVdcStorageProfile",
   816  		"filter":        fmt.Sprintf("name==%s", url.QueryEscape(name)),
   817  		"filterEncoded": "true",
   818  	})
   819  	if err != nil {
   820  		return nil, err
   821  	}
   822  
   823  	return results.Results.ProviderVdcStorageProfileRecord, nil
   824  }
   825  
   826  // QueryNetworkPoolByName finds a network pool by name
   827  func QueryNetworkPoolByName(vcdCli *VCDClient, name string) ([]*types.QueryResultNetworkPoolRecordType, error) {
   828  	results, err := vcdCli.Client.cumulativeQuery(types.QtNetworkPool, nil, map[string]string{
   829  		"type":          types.QtNetworkPool,
   830  		"filter":        fmt.Sprintf("name==%s", url.QueryEscape(name)),
   831  		"filterEncoded": "true",
   832  	})
   833  	if err != nil {
   834  		return nil, err
   835  	}
   836  
   837  	return results.Results.NetworkPoolRecord, nil
   838  }
   839  
   840  // QueryProviderVdcByName finds a provider VDC by name
   841  func QueryProviderVdcByName(vcdCli *VCDClient, name string) ([]*types.QueryResultVMWProviderVdcRecordType, error) {
   842  	results, err := vcdCli.QueryWithNotEncodedParams(nil, map[string]string{
   843  		"type":          "providerVdc",
   844  		"filter":        fmt.Sprintf("name==%s", url.QueryEscape(name)),
   845  		"filterEncoded": "true",
   846  	})
   847  	if err != nil {
   848  		return nil, err
   849  	}
   850  
   851  	return results.Results.VMWProviderVdcRecord, nil
   852  }
   853  
   854  // QueryProviderVdcs gets the list of available provider VDCs
   855  func (vcdClient *VCDClient) QueryProviderVdcs() ([]*types.QueryResultVMWProviderVdcRecordType, error) {
   856  	results, err := vcdClient.QueryWithNotEncodedParams(nil, map[string]string{
   857  		"type": "providerVdc",
   858  	})
   859  	if err != nil {
   860  		return nil, err
   861  	}
   862  
   863  	return results.Results.VMWProviderVdcRecord, nil
   864  }
   865  
   866  // QueryNetworkPools gets the list of network pools
   867  func (vcdClient *VCDClient) QueryNetworkPools() ([]*types.QueryResultNetworkPoolRecordType, error) {
   868  	results, err := vcdClient.Client.cumulativeQuery(types.QtNetworkPool, nil, map[string]string{"type": types.QtNetworkPool})
   869  	if err != nil {
   870  		return nil, err
   871  	}
   872  
   873  	return results.Results.NetworkPoolRecord, nil
   874  }
   875  
   876  // QueryProviderVdcStorageProfiles gets the list of provider VDC storage profiles from ALL provider VDCs
   877  // Deprecated: use either client.QueryProviderVdcStorageProfiles or client.QueryAllProviderVdcStorageProfiles
   878  func (vcdClient *VCDClient) QueryProviderVdcStorageProfiles() ([]*types.QueryResultProviderVdcStorageProfileRecordType, error) {
   879  	return vcdClient.Client.QueryAllProviderVdcStorageProfiles()
   880  }
   881  
   882  // QueryAllProviderVdcStorageProfiles gets the list of provider VDC storage profiles from ALL provider VDCs
   883  func (client *Client) QueryAllProviderVdcStorageProfiles() ([]*types.QueryResultProviderVdcStorageProfileRecordType, error) {
   884  	results, err := client.cumulativeQuery(types.QtProviderVdcStorageProfile, nil, map[string]string{"type": types.QtProviderVdcStorageProfile})
   885  	if err != nil {
   886  		return nil, err
   887  	}
   888  
   889  	return results.Results.ProviderVdcStorageProfileRecord, nil
   890  }
   891  
   892  // QueryProviderVdcStorageProfiles gets the list of provider VDC storage profiles for a given Provider VDC
   893  func (client *Client) QueryProviderVdcStorageProfiles(providerVdcHref string) ([]*types.QueryResultProviderVdcStorageProfileRecordType, error) {
   894  	results, err := client.cumulativeQuery(types.QtProviderVdcStorageProfile, nil, map[string]string{
   895  		"type":   types.QtProviderVdcStorageProfile,
   896  		"filter": fmt.Sprintf("providerVdc==%s", providerVdcHref),
   897  	})
   898  	if err != nil {
   899  		return nil, err
   900  	}
   901  
   902  	return results.Results.ProviderVdcStorageProfileRecord, nil
   903  }
   904  
   905  // QueryCompatibleStorageProfiles retrieves all storage profiles belonging to the same provider VDC to which
   906  // the Org VDC belongs
   907  func (adminVdc *AdminVdc) QueryCompatibleStorageProfiles() ([]*types.QueryResultProviderVdcStorageProfileRecordType, error) {
   908  	return adminVdc.client.QueryProviderVdcStorageProfiles(adminVdc.AdminVdc.ProviderVdcReference.HREF)
   909  }
   910  
   911  // GetNetworkPoolByHREF functions fetches an network pool using VDC client and network pool href
   912  func GetNetworkPoolByHREF(client *VCDClient, href string) (*types.VMWNetworkPool, error) {
   913  	util.Logger.Printf("[TRACE] Get network pool by HREF: %s\n", href)
   914  
   915  	networkPool := &types.VMWNetworkPool{}
   916  
   917  	_, err := client.Client.ExecuteRequest(href, http.MethodGet,
   918  		"", "error fetching network pool: %s", nil, networkPool)
   919  
   920  	// Return the disk
   921  	return networkPool, err
   922  
   923  }
   924  
   925  // QueryOrgVdcNetworkByName finds a org VDC network by name which has edge gateway as reference
   926  func QueryOrgVdcNetworkByName(vcdCli *VCDClient, name string) ([]*types.QueryResultOrgVdcNetworkRecordType, error) {
   927  	results, err := vcdCli.QueryWithNotEncodedParams(nil, map[string]string{
   928  		"type":          "orgVdcNetwork",
   929  		"filter":        fmt.Sprintf("name==%s", url.QueryEscape(name)),
   930  		"filterEncoded": "true",
   931  	})
   932  	if err != nil {
   933  		return nil, err
   934  	}
   935  
   936  	return results.Results.OrgVdcNetworkRecord, nil
   937  }
   938  
   939  // QueryAllVdcs returns all Org VDCs in a VCD instance
   940  //
   941  // This function requires "System" user or returns an error
   942  func (client *Client) QueryAllVdcs() ([]*types.QueryResultOrgVdcRecordType, error) {
   943  	if !client.IsSysAdmin {
   944  		return nil, errors.New("this function only works with 'System' user")
   945  	}
   946  	return queryOrgVdcList(client, nil)
   947  }
   948  
   949  // QueryNsxtManagerByName searches for NSX-T managers available in VCD
   950  func (vcdClient *VCDClient) QueryNsxtManagerByName(name string) ([]*types.QueryResultNsxtManagerRecordType, error) {
   951  	results, err := vcdClient.QueryWithNotEncodedParams(nil, map[string]string{
   952  		"type":          "nsxTManager",
   953  		"filter":        fmt.Sprintf("name==%s", url.QueryEscape(name)),
   954  		"filterEncoded": "true",
   955  	})
   956  	if err != nil {
   957  		return nil, err
   958  	}
   959  
   960  	return results.Results.NsxtManagerRecord, nil
   961  }
   962  
   963  // QueryNsxtManagers retrieves all NSX-T managers available in VCD
   964  func (vcdClient *VCDClient) QueryNsxtManagers() ([]*types.QueryResultNsxtManagerRecordType, error) {
   965  	results, err := vcdClient.QueryWithNotEncodedParams(nil, map[string]string{
   966  		"type": "nsxTManager",
   967  	})
   968  	if err != nil {
   969  		return nil, err
   970  	}
   971  
   972  	return results.Results.NsxtManagerRecord, nil
   973  }
   974  
   975  // QueryNsxtManagerByHref searches for NSX-T managers available in VCD
   976  func (vcdClient *VCDClient) QueryNsxtManagerByHref(href string) ([]*types.QueryResultNsxtManagerRecordType, error) {
   977  	results, err := vcdClient.QueryWithNotEncodedParams(nil, map[string]string{
   978  		"type":          "nsxTManager",
   979  		"filter":        fmt.Sprintf("href==%s", extractUuid(href)),
   980  		"filterEncoded": "true",
   981  	})
   982  	if err != nil {
   983  		return nil, err
   984  	}
   985  
   986  	return results.Results.NsxtManagerRecord, nil
   987  }
   988  
   989  // GetOrgByName finds an Organization by name
   990  // On success, returns a pointer to the Org structure and a nil error
   991  // On failure, returns a nil pointer and an error
   992  func (vcdClient *VCDClient) GetOrgByName(orgName string) (*Org, error) {
   993  	orgUrl, err := getOrgHREF(vcdClient, orgName)
   994  	if err != nil {
   995  		// Since this operation is a lookup from a list, we return the standard ErrorEntityNotFound
   996  		return nil, ErrorEntityNotFound
   997  	}
   998  	org := NewOrg(&vcdClient.Client)
   999  
  1000  	_, err = vcdClient.Client.ExecuteRequest(orgUrl, http.MethodGet,
  1001  		"", "error retrieving org: %s", nil, org.Org)
  1002  	if err != nil {
  1003  		return nil, err
  1004  	}
  1005  	org.TenantContext = &TenantContext{
  1006  		OrgId:   extractUuid(org.Org.ID),
  1007  		OrgName: org.Org.Name,
  1008  	}
  1009  	return org, nil
  1010  }
  1011  
  1012  // GetOrgById finds an Organization by ID
  1013  // On success, returns a pointer to the Org structure and a nil error
  1014  // On failure, returns a nil pointer and an error
  1015  func (vcdClient *VCDClient) GetOrgById(orgId string) (*Org, error) {
  1016  	orgUrl, err := getOrgHREFById(vcdClient, orgId)
  1017  	if err != nil {
  1018  		// Since this operation is a lookup from a list, we return the standard ErrorEntityNotFound
  1019  		return nil, ErrorEntityNotFound
  1020  	}
  1021  	org := NewOrg(&vcdClient.Client)
  1022  
  1023  	_, err = vcdClient.Client.ExecuteRequest(orgUrl, http.MethodGet,
  1024  		"", "error retrieving org list: %s", nil, org.Org)
  1025  	if err != nil {
  1026  		return nil, err
  1027  	}
  1028  	org.TenantContext = &TenantContext{
  1029  		OrgId:   extractUuid(org.Org.ID),
  1030  		OrgName: org.Org.Name,
  1031  	}
  1032  	return org, nil
  1033  }
  1034  
  1035  // GetOrgByNameOrId finds an Organization by name or ID
  1036  // On success, returns a pointer to the Org structure and a nil error
  1037  // On failure, returns a nil pointer and an error
  1038  func (vcdClient *VCDClient) GetOrgByNameOrId(identifier string) (*Org, error) {
  1039  	getByName := func(name string, refresh bool) (interface{}, error) { return vcdClient.GetOrgByName(name) }
  1040  	getById := func(id string, refresh bool) (interface{}, error) { return vcdClient.GetOrgById(id) }
  1041  	entity, err := getEntityByNameOrId(getByName, getById, identifier, false)
  1042  	if entity == nil {
  1043  		return nil, err
  1044  	}
  1045  	return entity.(*Org), err
  1046  }
  1047  
  1048  // GetAdminOrgByName finds an Admin Organization by name
  1049  // On success, returns a pointer to the Admin Org structure and a nil error
  1050  // On failure, returns a nil pointer and an error
  1051  func (vcdClient *VCDClient) GetAdminOrgByName(orgName string) (*AdminOrg, error) {
  1052  	orgUrl, err := getOrgHREF(vcdClient, orgName)
  1053  	if err != nil {
  1054  		return nil, ErrorEntityNotFound
  1055  	}
  1056  	orgHREF := vcdClient.Client.VCDHREF
  1057  	orgHREF.Path += "/admin/org/" + strings.Split(orgUrl, "/api/org/")[1]
  1058  
  1059  	adminOrg := NewAdminOrg(&vcdClient.Client)
  1060  
  1061  	_, err = vcdClient.Client.ExecuteRequest(orgHREF.String(), http.MethodGet,
  1062  		"", "error retrieving org: %s", nil, adminOrg.AdminOrg)
  1063  	if err != nil {
  1064  		return nil, err
  1065  	}
  1066  	adminOrg.TenantContext = &TenantContext{
  1067  		OrgId:   extractUuid(adminOrg.AdminOrg.ID),
  1068  		OrgName: adminOrg.AdminOrg.Name,
  1069  	}
  1070  
  1071  	return adminOrg, nil
  1072  }
  1073  
  1074  // GetAdminOrgById finds an Admin Organization by ID
  1075  // On success, returns a pointer to the Admin Org structure and a nil error
  1076  // On failure, returns a nil pointer and an error
  1077  func (vcdClient *VCDClient) GetAdminOrgById(orgId string) (*AdminOrg, error) {
  1078  	orgUrl, err := getOrgHREFById(vcdClient, orgId)
  1079  	if err != nil {
  1080  		return nil, ErrorEntityNotFound
  1081  	}
  1082  	orgHREF := vcdClient.Client.VCDHREF
  1083  	orgHREF.Path += "/admin/org/" + strings.Split(orgUrl, "/api/org/")[1]
  1084  
  1085  	adminOrg := NewAdminOrg(&vcdClient.Client)
  1086  
  1087  	_, err = vcdClient.Client.ExecuteRequest(orgHREF.String(), http.MethodGet,
  1088  		"", "error retrieving org: %s", nil, adminOrg.AdminOrg)
  1089  	if err != nil {
  1090  		return nil, err
  1091  	}
  1092  	adminOrg.TenantContext = &TenantContext{
  1093  		OrgId:   extractUuid(adminOrg.AdminOrg.ID),
  1094  		OrgName: adminOrg.AdminOrg.Name,
  1095  	}
  1096  	return adminOrg, nil
  1097  }
  1098  
  1099  // GetAdminOrgByNameOrId finds an Admin Organization by name or ID
  1100  // On success, returns a pointer to the Admin Org structure and a nil error
  1101  // On failure, returns a nil pointer and an error
  1102  func (vcdClient *VCDClient) GetAdminOrgByNameOrId(identifier string) (*AdminOrg, error) {
  1103  	getByName := func(name string, refresh bool) (interface{}, error) { return vcdClient.GetAdminOrgByName(name) }
  1104  	getById := func(id string, refresh bool) (interface{}, error) { return vcdClient.GetAdminOrgById(id) }
  1105  	entity, err := getEntityByNameOrId(getByName, getById, identifier, false)
  1106  	if entity == nil {
  1107  		return nil, err
  1108  	}
  1109  	return entity.(*AdminOrg), err
  1110  }
  1111  
  1112  // Returns the UUID part of an HREF
  1113  // Similar to getBareEntityUuid, but tailored to HREF
  1114  func GetUuidFromHref(href string, idAtEnd bool) (string, error) {
  1115  	util.Logger.Printf("[TRACE] GetUuidFromHref got href: %s with idAtEnd: %t", href, idAtEnd)
  1116  	// Regular expression to match an ID:
  1117  	//     1 string starting by 'https://' and ending with a '/',
  1118  	//     followed by
  1119  	//        1 group of 8 hexadecimal digits
  1120  	//        3 groups of 4 hexadecimal digits
  1121  	//        1 group of 12 hexadecimal digits
  1122  
  1123  	searchExpression := `^https://.+/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})`
  1124  	if idAtEnd {
  1125  		searchExpression += `$`
  1126  	} else {
  1127  		searchExpression += `.*$`
  1128  	}
  1129  	reGetID := regexp.MustCompile(searchExpression)
  1130  	matchList := reGetID.FindAllStringSubmatch(href, -1)
  1131  
  1132  	if len(matchList) == 0 || len(matchList[0]) < 2 {
  1133  		return "", fmt.Errorf("error extracting UUID from '%s'", href)
  1134  	}
  1135  	util.Logger.Printf("[TRACE] GetUuidFromHref returns UUID : %s", matchList[0][1])
  1136  	return matchList[0][1], nil
  1137  }
  1138  
  1139  // GetOrgList returns the list ov available orgs
  1140  func (vcdClient *VCDClient) GetOrgList() (*types.OrgList, error) {
  1141  	orgListHREF := vcdClient.Client.VCDHREF
  1142  	orgListHREF.Path += "/org"
  1143  
  1144  	orgList := new(types.OrgList)
  1145  
  1146  	_, err := vcdClient.Client.ExecuteRequest(orgListHREF.String(), http.MethodGet,
  1147  		"", "error getting list of organizations: %s", nil, orgList)
  1148  	if err != nil {
  1149  		return nil, err
  1150  	}
  1151  	return orgList, nil
  1152  }
  1153  
  1154  // QueryAdminOrgVdcStorageProfileByID finds a StorageProfile of VDC by ID as admin
  1155  func QueryAdminOrgVdcStorageProfileByID(vcdCli *VCDClient, id string) (*types.QueryResultAdminOrgVdcStorageProfileRecordType, error) {
  1156  	if !vcdCli.Client.IsSysAdmin {
  1157  		return nil, errors.New("can't query type QueryAdminOrgVdcStorageProfileByID as tenant user")
  1158  	}
  1159  	results, err := vcdCli.QueryWithNotEncodedParams(nil, map[string]string{
  1160  		"type":          types.QtAdminOrgVdcStorageProfile,
  1161  		"filter":        fmt.Sprintf("id==%s", url.QueryEscape(id)),
  1162  		"filterEncoded": "true",
  1163  	})
  1164  	if err != nil {
  1165  		return nil, err
  1166  	}
  1167  	if len(results.Results.AdminOrgVdcStorageProfileRecord) == 0 {
  1168  		return nil, ErrorEntityNotFound
  1169  	}
  1170  	if len(results.Results.AdminOrgVdcStorageProfileRecord) > 1 {
  1171  		return nil, fmt.Errorf("more than one Storage Profile found with ID %s", id)
  1172  	}
  1173  	return results.Results.AdminOrgVdcStorageProfileRecord[0], nil
  1174  }
  1175  
  1176  // queryAdminOrgVdcStorageProfilesByVdcId finds all Storage Profiles of a VDC
  1177  func queryAdminOrgVdcStorageProfilesByVdcId(client *Client, vdcId string) ([]*types.QueryResultAdminOrgVdcStorageProfileRecordType, error) {
  1178  	if !client.IsSysAdmin {
  1179  		return nil, errors.New("can't query type QueryResultAdminOrgVdcStorageProfileRecordType as Tenant user")
  1180  	}
  1181  	results, err := client.QueryWithNotEncodedParams(nil, map[string]string{
  1182  		"type":          types.QtAdminOrgVdcStorageProfile,
  1183  		"filter":        fmt.Sprintf("vdc==%s", url.QueryEscape(vdcId)),
  1184  		"filterEncoded": "true",
  1185  	})
  1186  	if err != nil {
  1187  		return nil, err
  1188  	}
  1189  	return results.Results.AdminOrgVdcStorageProfileRecord, nil
  1190  }
  1191  
  1192  // queryOrgVdcStorageProfilesByVdcId finds all Storage Profiles of a VDC
  1193  func queryOrgVdcStorageProfilesByVdcId(client *Client, vdcId string) ([]*types.QueryResultOrgVdcStorageProfileRecordType, error) {
  1194  	if client.IsSysAdmin {
  1195  		return nil, errors.New("can't query type QueryResultAdminOrgVdcStorageProfileRecordType as System administrator")
  1196  	}
  1197  	results, err := client.QueryWithNotEncodedParams(nil, map[string]string{
  1198  		"type":          types.QtOrgVdcStorageProfile,
  1199  		"filter":        fmt.Sprintf("vdc==%s", url.QueryEscape(vdcId)),
  1200  		"filterEncoded": "true",
  1201  	})
  1202  	if err != nil {
  1203  		return nil, err
  1204  	}
  1205  	return results.Results.OrgVdcStorageProfileRecord, nil
  1206  }
  1207  
  1208  // QueryOrgVdcStorageProfileByID finds a StorageProfile of VDC by ID
  1209  func QueryOrgVdcStorageProfileByID(vcdCli *VCDClient, id string) (*types.QueryResultOrgVdcStorageProfileRecordType, error) {
  1210  	if vcdCli.Client.IsSysAdmin {
  1211  		return nil, errors.New("can't query type QueryOrgVdcStorageProfileByID as System administrator")
  1212  	}
  1213  	results, err := vcdCli.QueryWithNotEncodedParams(nil, map[string]string{
  1214  		"type":          types.QtOrgVdcStorageProfile,
  1215  		"filter":        fmt.Sprintf("id==%s", url.QueryEscape(id)),
  1216  		"filterEncoded": "true",
  1217  	})
  1218  	if err != nil {
  1219  		return nil, err
  1220  	}
  1221  	if len(results.Results.OrgVdcStorageProfileRecord) == 0 {
  1222  		return nil, ErrorEntityNotFound
  1223  	}
  1224  	if len(results.Results.OrgVdcStorageProfileRecord) > 1 {
  1225  		return nil, fmt.Errorf("more than one Storage Profile found with ID %s", id)
  1226  	}
  1227  	return results.Results.OrgVdcStorageProfileRecord[0], nil
  1228  }
  1229  
  1230  // GetGlobalDefaultSegmentProfileTemplates retrieves VCD global configuration for Segment Profile Templates
  1231  func (vcdClient *VCDClient) GetGlobalDefaultSegmentProfileTemplates() (*types.NsxtGlobalDefaultSegmentProfileTemplate, error) {
  1232  	c := crudConfig{
  1233  		endpoint:    types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointNsxtGlobalDefaultSegmentProfileTemplates,
  1234  		entityLabel: labelGlobalDefaultSegmentProfileTemplate,
  1235  	}
  1236  
  1237  	return getInnerEntity[types.NsxtGlobalDefaultSegmentProfileTemplate](&vcdClient.Client, c)
  1238  }
  1239  
  1240  // UpdateGlobalDefaultSegmentProfileTemplates updates VCD global configuration for Segment Profile Templates
  1241  func (vcdClient *VCDClient) UpdateGlobalDefaultSegmentProfileTemplates(entityConfig *types.NsxtGlobalDefaultSegmentProfileTemplate) (*types.NsxtGlobalDefaultSegmentProfileTemplate, error) {
  1242  	c := crudConfig{
  1243  		endpoint:    types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointNsxtGlobalDefaultSegmentProfileTemplates,
  1244  		entityLabel: labelGlobalDefaultSegmentProfileTemplate,
  1245  	}
  1246  	return updateInnerEntity(&vcdClient.Client, c, entityConfig)
  1247  }