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 }