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