github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/vdc.go (about) 1 /* 2 * Copyright 2023 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. 3 */ 4 5 package govcd 6 7 import ( 8 "errors" 9 "fmt" 10 "net/http" 11 "net/url" 12 "strconv" 13 "strings" 14 "time" 15 16 "github.com/vmware/go-vcloud-director/v2/types/v56" 17 "github.com/vmware/go-vcloud-director/v2/util" 18 ) 19 20 type Vdc struct { 21 Vdc *types.Vdc 22 client *Client 23 parent organization 24 } 25 26 func NewVdc(cli *Client) *Vdc { 27 return &Vdc{ 28 Vdc: new(types.Vdc), 29 client: cli, 30 } 31 } 32 33 // Gets a vapp with a specific url vappHREF 34 func (vdc *Vdc) getVdcVAppbyHREF(vappHREF *url.URL) (*VApp, error) { 35 vapp := NewVApp(vdc.client) 36 37 _, err := vdc.client.ExecuteRequest(vappHREF.String(), http.MethodGet, 38 "", "error retrieving VApp: %s", nil, vapp.VApp) 39 40 return vapp, err 41 } 42 43 // Undeploys every vapp in the vdc 44 func (vdc *Vdc) undeployAllVdcVApps() error { 45 err := vdc.Refresh() 46 if err != nil { 47 return fmt.Errorf("error refreshing vdc: %s", err) 48 } 49 for _, resents := range vdc.Vdc.ResourceEntities { 50 for _, resent := range resents.ResourceEntity { 51 if resent.Type == "application/vnd.vmware.vcloud.vApp+xml" { 52 vappHREF, err := url.Parse(resent.HREF) 53 if err != nil { 54 return err 55 } 56 vapp, err := vdc.getVdcVAppbyHREF(vappHREF) 57 if err != nil { 58 return fmt.Errorf("error retrieving vapp with url: %s and with error %s", vappHREF.Path, err) 59 } 60 task, err := vapp.Undeploy() 61 if err != nil { 62 return err 63 } 64 if task == (Task{}) { 65 continue 66 } 67 err = task.WaitTaskCompletion() 68 if err != nil { 69 return err 70 } 71 } 72 } 73 } 74 return nil 75 } 76 77 // Removes all vapps in the vdc 78 func (vdc *Vdc) removeAllVdcVApps() error { 79 err := vdc.Refresh() 80 if err != nil { 81 return fmt.Errorf("error refreshing vdc: %s", err) 82 } 83 for _, resents := range vdc.Vdc.ResourceEntities { 84 for _, resent := range resents.ResourceEntity { 85 if resent.Type == "application/vnd.vmware.vcloud.vApp+xml" { 86 vappHREF, err := url.Parse(resent.HREF) 87 if err != nil { 88 return err 89 } 90 vapp, err := vdc.getVdcVAppbyHREF(vappHREF) 91 if err != nil { 92 return fmt.Errorf("error retrieving vapp with url: %s and with error %s", vappHREF.Path, err) 93 } 94 task, err := vapp.Delete() 95 if err != nil { 96 return fmt.Errorf("error deleting vapp: %s", err) 97 } 98 err = task.WaitTaskCompletion() 99 if err != nil { 100 return fmt.Errorf("couldn't finish removing vapp %s", err) 101 } 102 } 103 } 104 } 105 return nil 106 } 107 108 func (vdc *Vdc) Refresh() error { 109 110 if vdc.Vdc.HREF == "" { 111 return fmt.Errorf("cannot refresh, Object is empty") 112 } 113 114 // Empty struct before a new unmarshal, otherwise we end up with duplicate 115 // elements in slices. 116 unmarshalledVdc := &types.Vdc{} 117 118 _, err := vdc.client.ExecuteRequest(vdc.Vdc.HREF, http.MethodGet, 119 "", "error refreshing vDC: %s", nil, unmarshalledVdc) 120 if err != nil { 121 return err 122 } 123 124 vdc.Vdc = unmarshalledVdc 125 126 // The request was successful 127 return nil 128 } 129 130 // Deletes the vdc, returning an error of the vCD call fails. 131 // API Documentation: https://code.vmware.com/apis/220/vcloud#/doc/doc/operations/DELETE-Vdc.html 132 func (vdc *Vdc) Delete(force bool, recursive bool) (Task, error) { 133 util.Logger.Printf("[TRACE] Vdc.Delete - deleting VDC with force: %t, recursive: %t", force, recursive) 134 135 if vdc.Vdc.HREF == "" { 136 return Task{}, fmt.Errorf("cannot delete, Object is empty") 137 } 138 139 vdcUrl, err := url.ParseRequestURI(vdc.Vdc.HREF) 140 if err != nil { 141 return Task{}, fmt.Errorf("error parsing vdc url: %s", err) 142 } 143 144 req := vdc.client.NewRequest(map[string]string{ 145 "force": strconv.FormatBool(force), 146 "recursive": strconv.FormatBool(recursive), 147 }, http.MethodDelete, *vdcUrl, nil) 148 resp, err := checkResp(vdc.client.Http.Do(req)) 149 if err != nil { 150 return Task{}, fmt.Errorf("error deleting vdc: %s", err) 151 } 152 task := NewTask(vdc.client) 153 if err = decodeBody(types.BodyTypeXML, resp, task.Task); err != nil { 154 return Task{}, fmt.Errorf("error decoding task response: %s", err) 155 } 156 if task.Task.Status == "error" { 157 return Task{}, fmt.Errorf("vdc not properly destroyed") 158 } 159 return *task, nil 160 } 161 162 // Deletes the vdc and waits for the asynchronous task to complete. 163 func (vdc *Vdc) DeleteWait(force bool, recursive bool) error { 164 task, err := vdc.Delete(force, recursive) 165 if err != nil { 166 return err 167 } 168 err = task.WaitTaskCompletion() 169 if err != nil { 170 return fmt.Errorf("couldn't finish removing vdc %s", err) 171 } 172 return nil 173 } 174 175 // Deprecated: use GetOrgVdcNetworkByName 176 func (vdc *Vdc) FindVDCNetwork(network string) (OrgVDCNetwork, error) { 177 178 err := vdc.Refresh() 179 if err != nil { 180 return OrgVDCNetwork{}, fmt.Errorf("error refreshing vdc: %s", err) 181 } 182 for _, an := range vdc.Vdc.AvailableNetworks { 183 for _, reference := range an.Network { 184 if reference.Name == network { 185 orgNet := NewOrgVDCNetwork(vdc.client) 186 187 _, err := vdc.client.ExecuteRequest(reference.HREF, http.MethodGet, 188 "", "error retrieving org vdc network: %s", nil, orgNet.OrgVDCNetwork) 189 190 // The request was successful 191 return *orgNet, err 192 193 } 194 } 195 } 196 197 return OrgVDCNetwork{}, fmt.Errorf("can't find VDC Network: %s", network) 198 } 199 200 // GetOrgVdcNetworkByHref returns an Org VDC Network reference if the network HREF matches an existing one. 201 // If no valid external network is found, it returns a nil Network reference and an error 202 func (vdc *Vdc) GetOrgVdcNetworkByHref(href string) (*OrgVDCNetwork, error) { 203 204 orgNet := NewOrgVDCNetwork(vdc.client) 205 206 _, err := vdc.client.ExecuteRequest(href, http.MethodGet, 207 "", "error retrieving org vdc network: %s", nil, orgNet.OrgVDCNetwork) 208 209 // The request was successful 210 return orgNet, err 211 } 212 213 // GetOrgVdcNetworkByName returns an Org VDC Network reference if the network name matches an existing one. 214 // If no valid external network is found, it returns a nil Network reference and an error 215 func (vdc *Vdc) GetOrgVdcNetworkByName(name string, refresh bool) (*OrgVDCNetwork, error) { 216 if refresh { 217 err := vdc.Refresh() 218 if err != nil { 219 return nil, fmt.Errorf("error refreshing vdc: %s", err) 220 } 221 } 222 for _, an := range vdc.Vdc.AvailableNetworks { 223 for _, reference := range an.Network { 224 if reference.Name == name { 225 return vdc.GetOrgVdcNetworkByHref(reference.HREF) 226 } 227 } 228 } 229 230 return nil, ErrorEntityNotFound 231 } 232 233 // GetOrgVdcNetworkById returns an Org VDC Network reference if the network ID matches an existing one. 234 // If no valid external network is found, it returns a nil Network reference and an error 235 func (vdc *Vdc) GetOrgVdcNetworkById(id string, refresh bool) (*OrgVDCNetwork, error) { 236 if refresh { 237 err := vdc.Refresh() 238 if err != nil { 239 return nil, fmt.Errorf("error refreshing vdc: %s", err) 240 } 241 } 242 for _, an := range vdc.Vdc.AvailableNetworks { 243 for _, reference := range an.Network { 244 // Some versions of vCD do not return an ID in the network reference 245 // We use equalIds to overcome this issue 246 if equalIds(id, reference.ID, reference.HREF) { 247 return vdc.GetOrgVdcNetworkByHref(reference.HREF) 248 } 249 } 250 } 251 252 return nil, ErrorEntityNotFound 253 } 254 255 // GetOrgVdcNetworkByNameOrId returns a VDC Network reference if either the network name or ID matches an existing one. 256 // If no valid external network is found, it returns a nil ExternalNetwork reference and an error 257 func (vdc *Vdc) GetOrgVdcNetworkByNameOrId(identifier string, refresh bool) (*OrgVDCNetwork, error) { 258 getByName := func(name string, refresh bool) (interface{}, error) { return vdc.GetOrgVdcNetworkByName(name, refresh) } 259 getById := func(id string, refresh bool) (interface{}, error) { return vdc.GetOrgVdcNetworkById(id, refresh) } 260 entity, err := getEntityByNameOrId(getByName, getById, identifier, false) 261 if entity == nil { 262 return nil, err 263 } 264 return entity.(*OrgVDCNetwork), err 265 } 266 267 func (vdc *Vdc) FindStorageProfileReference(name string) (types.Reference, error) { 268 269 err := vdc.Refresh() 270 if err != nil { 271 return types.Reference{}, fmt.Errorf("error refreshing vdc: %s", err) 272 } 273 for _, sp := range vdc.Vdc.VdcStorageProfiles.VdcStorageProfile { 274 if sp.Name == name { 275 return types.Reference{HREF: sp.HREF, Name: sp.Name, ID: sp.ID}, nil 276 } 277 } 278 return types.Reference{}, fmt.Errorf("can't find any VDC Storage_profiles") 279 } 280 281 // GetDefaultStorageProfileReference should find the default storage profile for a VDC 282 // Deprecated: unused and implemented in the wrong way. Use adminVdc.GetDefaultStorageProfileReference instead 283 func (vdc *Vdc) GetDefaultStorageProfileReference(storageprofiles *types.QueryResultRecordsType) (types.Reference, error) { 284 285 err := vdc.Refresh() 286 if err != nil { 287 return types.Reference{}, fmt.Errorf("error refreshing vdc: %s", err) 288 } 289 for _, spr := range storageprofiles.OrgVdcStorageProfileRecord { 290 if spr.IsDefaultStorageProfile { 291 return types.Reference{HREF: spr.HREF, Name: spr.Name}, nil 292 } 293 } 294 return types.Reference{}, fmt.Errorf("can't find Default VDC Storage_profile") 295 } 296 297 // Deprecated: use GetEdgeGatewayByName 298 func (vdc *Vdc) FindEdgeGateway(edgegateway string) (EdgeGateway, error) { 299 300 err := vdc.Refresh() 301 if err != nil { 302 return EdgeGateway{}, fmt.Errorf("error refreshing vdc: %s", err) 303 } 304 for _, av := range vdc.Vdc.Link { 305 if av.Rel == "edgeGateways" && av.Type == types.MimeQueryRecords { 306 307 query := new(types.QueryResultEdgeGatewayRecordsType) 308 309 _, err := vdc.client.ExecuteRequest(av.HREF, http.MethodGet, 310 "", "error querying edge gateways: %s", nil, query) 311 if err != nil { 312 return EdgeGateway{}, err 313 } 314 315 var href string 316 317 for _, edge := range query.EdgeGatewayRecord { 318 if edge.Name == edgegateway { 319 href = edge.HREF 320 } 321 } 322 323 if href == "" { 324 return EdgeGateway{}, fmt.Errorf("can't find edge gateway with name: %s", edgegateway) 325 } 326 327 edge := NewEdgeGateway(vdc.client) 328 329 _, err = vdc.client.ExecuteRequest(href, http.MethodGet, 330 "", "error retrieving edge gateway: %s", nil, edge.EdgeGateway) 331 332 // TODO - remove this if a solution is found or once 9.7 is deprecated 333 // vCD 9.7 has a bug and sometimes it fails to retrieve edge gateway with weird error. 334 // At this point in time the solution is to retry a few times as it does not fail to 335 // retrieve when retried. 336 // 337 // GitHUB issue - https://github.com/vmware/go-vcloud-director/issues/218 338 if err != nil { 339 util.Logger.Printf("[DEBUG] vCD 9.7 is known to sometimes respond with error on edge gateway (%s) "+ 340 "retrieval. As a workaround this is done a few times before failing. Retrying: ", edgegateway) 341 for i := 1; i < 4 && err != nil; i++ { 342 time.Sleep(200 * time.Millisecond) 343 util.Logger.Printf("%d ", i) 344 _, err = vdc.client.ExecuteRequest(href, http.MethodGet, 345 "", "error retrieving edge gateway: %s", nil, edge.EdgeGateway) 346 } 347 util.Logger.Printf("\n") 348 } 349 350 return *edge, err 351 352 } 353 } 354 return EdgeGateway{}, fmt.Errorf("can't find Edge Gateway") 355 356 } 357 358 // GetEdgeGatewayByHref retrieves an edge gateway from VDC 359 // by querying directly its HREF. 360 // The name passed as parameter is only used for error reporting 361 func (vdc *Vdc) GetEdgeGatewayByHref(href string) (*EdgeGateway, error) { 362 if href == "" { 363 return nil, fmt.Errorf("empty edge gateway HREF") 364 } 365 366 edge := NewEdgeGateway(vdc.client) 367 368 _, err := vdc.client.ExecuteRequest(href, http.MethodGet, 369 "", "error retrieving edge gateway: %s", nil, edge.EdgeGateway) 370 371 // TODO - remove this if a solution is found or once 9.7 is deprecated 372 // vCD 9.7 has a bug and sometimes it fails to retrieve edge gateway with weird error. 373 // At this point in time the solution is to retry a few times as it does not fail to 374 // retrieve when retried. 375 // 376 // GitHUB issue - https://github.com/vmware/go-vcloud-director/issues/218 377 if err != nil { 378 util.Logger.Printf("[DEBUG] vCD 9.7 is known to sometimes respond with error on edge gateway " + 379 "retrieval. As a workaround this is done a few times before failing. Retrying:") 380 for i := 1; i < 4 && err != nil; i++ { 381 time.Sleep(200 * time.Millisecond) 382 util.Logger.Printf("%d ", i) 383 _, err = vdc.client.ExecuteRequest(href, http.MethodGet, 384 "", "error retrieving edge gateway: %s", nil, edge.EdgeGateway) 385 } 386 util.Logger.Printf("\n") 387 } 388 389 if err != nil { 390 return nil, err 391 } 392 393 // Edge gateways can sometimes come without any configured services which 394 // lead to nil pointer dereference when adding e.g a DNAT rule 395 // https://github.com/vmware/go-vcloud-director/issues/585 396 if edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration == nil { 397 edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration = &types.GatewayFeatures{} 398 } 399 400 return edge, nil 401 } 402 403 // QueryEdgeGatewayList returns a list of all the edge gateways in a VDC 404 func (vdc *Vdc) QueryEdgeGatewayList() ([]*types.QueryResultEdgeGatewayRecordType, error) { 405 results, err := vdc.client.cumulativeQuery(types.QtEdgeGateway, nil, map[string]string{ 406 "type": types.QtEdgeGateway, 407 "filter": fmt.Sprintf("orgVdcName==%s", url.QueryEscape(vdc.Vdc.Name)), 408 "filterEncoded": "true", 409 }) 410 if err != nil { 411 return nil, err 412 } 413 return results.Results.EdgeGatewayRecord, nil 414 } 415 416 // GetEdgeGatewayRecordsType retrieves a list of edge gateways from VDC 417 // Deprecated: use QueryEdgeGatewayList instead 418 func (vdc *Vdc) GetEdgeGatewayRecordsType(refresh bool) (*types.QueryResultEdgeGatewayRecordsType, error) { 419 items, err := vdc.QueryEdgeGatewayList() 420 if err != nil { 421 return nil, fmt.Errorf("error retrieving edge gateway list: %s", err) 422 } 423 return &types.QueryResultEdgeGatewayRecordsType{ 424 Total: float64(len(items)), 425 EdgeGatewayRecord: items, 426 }, nil 427 } 428 429 // GetEdgeGatewayByName search the VDC list of edge gateways for a given name. 430 // If the name matches, it returns a pointer to an edge gateway object. 431 // On failure, it returns a nil object and an error 432 func (vdc *Vdc) GetEdgeGatewayByName(name string, refresh bool) (*EdgeGateway, error) { 433 edgeGatewayList, err := vdc.QueryEdgeGatewayList() 434 if err != nil { 435 return nil, fmt.Errorf("error retrieving edge gateways list: %s", err) 436 } 437 438 for _, edge := range edgeGatewayList { 439 if edge.Name == name { 440 return vdc.GetEdgeGatewayByHref(edge.HREF) 441 } 442 } 443 444 return nil, ErrorEntityNotFound 445 } 446 447 // GetEdgeGatewayById search VDC list of edge gateways for a given ID. 448 // If the id matches, it returns a pointer to an edge gateway object. 449 // On failure, it returns a nil object and an error 450 func (vdc *Vdc) GetEdgeGatewayById(id string, refresh bool) (*EdgeGateway, error) { 451 edgeGatewayList, err := vdc.QueryEdgeGatewayList() 452 if err != nil { 453 return nil, fmt.Errorf("error retrieving edge gateways list: %s", err) 454 } 455 456 for _, edge := range edgeGatewayList { 457 if equalIds(id, "", edge.HREF) { 458 return vdc.GetEdgeGatewayByHref(edge.HREF) 459 } 460 } 461 462 return nil, ErrorEntityNotFound 463 } 464 465 // GetEdgeGatewayByNameOrId search the VDC list of edge gateways for a given name or ID. 466 // If the name or the ID match, it returns a pointer to an edge gateway object. 467 // On failure, it returns a nil object and an error 468 func (vdc *Vdc) GetEdgeGatewayByNameOrId(identifier string, refresh bool) (*EdgeGateway, error) { 469 getByName := func(name string, refresh bool) (interface{}, error) { return vdc.GetEdgeGatewayByName(name, refresh) } 470 getById := func(id string, refresh bool) (interface{}, error) { return vdc.GetEdgeGatewayById(id, refresh) } 471 entity, err := getEntityByNameOrId(getByName, getById, identifier, false) 472 if entity == nil { 473 return nil, err 474 } 475 return entity.(*EdgeGateway), err 476 } 477 478 // ComposeRawVApp creates an empty vApp 479 // Deprecated: use CreateRawVApp instead 480 func (vdc *Vdc) ComposeRawVApp(name string, description string) error { 481 vcomp := &types.ComposeVAppParams{ 482 Ovf: types.XMLNamespaceOVF, 483 Xsi: types.XMLNamespaceXSI, 484 Xmlns: types.XMLNamespaceVCloud, 485 Deploy: false, 486 Name: name, 487 PowerOn: false, 488 Description: description, 489 } 490 491 vdcHref, err := url.ParseRequestURI(vdc.Vdc.HREF) 492 if err != nil { 493 return fmt.Errorf("error getting vdc href: %s", err) 494 } 495 vdcHref.Path += "/action/composeVApp" 496 497 // This call is wrong: /action/composeVApp returns a vApp, not a task 498 task, err := vdc.client.ExecuteTaskRequest(vdcHref.String(), http.MethodPost, 499 types.MimeComposeVappParams, "error instantiating a new vApp:: %s", vcomp) 500 if err != nil { 501 return fmt.Errorf("error executing task request: %s", err) 502 } 503 504 err = task.WaitTaskCompletion() 505 if err != nil { 506 return fmt.Errorf("error performing task: %s", err) 507 } 508 509 return nil 510 } 511 512 // CreateRawVApp creates an empty vApp 513 func (vdc *Vdc) CreateRawVApp(name string, description string) (*VApp, error) { 514 vcomp := &types.ComposeVAppParams{ 515 Ovf: types.XMLNamespaceOVF, 516 Xsi: types.XMLNamespaceXSI, 517 Xmlns: types.XMLNamespaceVCloud, 518 Deploy: false, 519 Name: name, 520 PowerOn: false, 521 Description: description, 522 } 523 524 vdcHref, err := url.ParseRequestURI(vdc.Vdc.HREF) 525 if err != nil { 526 return nil, fmt.Errorf("error getting vdc href: %s", err) 527 } 528 vdcHref.Path += "/action/composeVApp" 529 530 var vAppContents types.VApp 531 532 _, err = vdc.client.ExecuteRequest(vdcHref.String(), http.MethodPost, 533 types.MimeComposeVappParams, "error instantiating a new vApp:: %s", vcomp, &vAppContents) 534 if err != nil { 535 return nil, fmt.Errorf("error executing task request: %s", err) 536 } 537 538 if vAppContents.Tasks != nil { 539 for _, innerTask := range vAppContents.Tasks.Task { 540 if innerTask != nil { 541 task := NewTask(vdc.client) 542 task.Task = innerTask 543 err = task.WaitTaskCompletion() 544 if err != nil { 545 return nil, fmt.Errorf("error performing task: %s", err) 546 } 547 } 548 } 549 } 550 551 vapp := NewVApp(vdc.client) 552 vapp.VApp = &vAppContents 553 554 err = vapp.Refresh() 555 if err != nil { 556 return nil, err 557 } 558 559 err = vdc.Refresh() 560 if err != nil { 561 return nil, err 562 } 563 return vapp, nil 564 } 565 566 // ComposeVApp creates a vapp with the given template, name, and description 567 // that uses the storageprofile and networks given. If you want all eulas 568 // to be accepted set acceptalleulas to true. Returns a successful task 569 // if completed successfully, otherwise returns an error and an empty task. 570 // Deprecated: bad implementation 571 func (vdc *Vdc) ComposeVApp(orgvdcnetworks []*types.OrgVDCNetwork, vapptemplate VAppTemplate, storageprofileref types.Reference, name string, description string, acceptalleulas bool) (Task, error) { 572 if vapptemplate.VAppTemplate.Children == nil || orgvdcnetworks == nil { 573 return Task{}, fmt.Errorf("can't compose a new vApp, objects passed are not valid") 574 } 575 576 // Determine primary network connection index number. We normally depend on it being inherited from vApp template 577 // but in the case when vApp template does not have network card it would fail on the index being undefined. We 578 // set the value to 0 (first NIC instead) 579 primaryNetworkConnectionIndex := 0 580 if vapptemplate.VAppTemplate.Children != nil && len(vapptemplate.VAppTemplate.Children.VM) > 0 && 581 vapptemplate.VAppTemplate.Children.VM[0].NetworkConnectionSection != nil { 582 primaryNetworkConnectionIndex = vapptemplate.VAppTemplate.Children.VM[0].NetworkConnectionSection.PrimaryNetworkConnectionIndex 583 } 584 585 // Build request XML 586 vcomp := &types.ComposeVAppParams{ 587 Ovf: types.XMLNamespaceOVF, 588 Xsi: types.XMLNamespaceXSI, 589 Xmlns: types.XMLNamespaceVCloud, 590 Deploy: false, 591 Name: name, 592 PowerOn: false, 593 Description: description, 594 InstantiationParams: &types.InstantiationParams{ 595 NetworkConfigSection: &types.NetworkConfigSection{ 596 Info: "Configuration parameters for logical networks", 597 }, 598 }, 599 AllEULAsAccepted: acceptalleulas, 600 SourcedItem: &types.SourcedCompositionItemParam{ 601 Source: &types.Reference{ 602 HREF: vapptemplate.VAppTemplate.Children.VM[0].HREF, 603 Name: vapptemplate.VAppTemplate.Children.VM[0].Name, 604 }, 605 InstantiationParams: &types.InstantiationParams{ 606 NetworkConnectionSection: &types.NetworkConnectionSection{ 607 Info: "Network config for sourced item", 608 PrimaryNetworkConnectionIndex: primaryNetworkConnectionIndex, 609 }, 610 }, 611 }, 612 } 613 for index, orgvdcnetwork := range orgvdcnetworks { 614 vcomp.InstantiationParams.NetworkConfigSection.NetworkConfig = append(vcomp.InstantiationParams.NetworkConfigSection.NetworkConfig, 615 types.VAppNetworkConfiguration{ 616 NetworkName: orgvdcnetwork.Name, 617 Configuration: &types.NetworkConfiguration{ 618 FenceMode: types.FenceModeBridged, 619 ParentNetwork: &types.Reference{ 620 HREF: orgvdcnetwork.HREF, 621 Name: orgvdcnetwork.Name, 622 Type: orgvdcnetwork.Type, 623 }, 624 }, 625 }, 626 ) 627 vcomp.SourcedItem.InstantiationParams.NetworkConnectionSection.NetworkConnection = append(vcomp.SourcedItem.InstantiationParams.NetworkConnectionSection.NetworkConnection, 628 &types.NetworkConnection{ 629 Network: orgvdcnetwork.Name, 630 NetworkConnectionIndex: index, 631 IsConnected: true, 632 IPAddressAllocationMode: types.IPAllocationModePool, 633 }, 634 ) 635 vcomp.SourcedItem.NetworkAssignment = append(vcomp.SourcedItem.NetworkAssignment, 636 &types.NetworkAssignment{ 637 InnerNetwork: orgvdcnetwork.Name, 638 ContainerNetwork: orgvdcnetwork.Name, 639 }, 640 ) 641 } 642 if storageprofileref.HREF != "" { 643 vcomp.SourcedItem.StorageProfile = &storageprofileref 644 } 645 646 vdcHref, err := url.ParseRequestURI(vdc.Vdc.HREF) 647 if err != nil { 648 return Task{}, fmt.Errorf("error getting vdc href: %s", err) 649 } 650 vdcHref.Path += "/action/composeVApp" 651 652 // Like ComposeRawVApp, this function returns a task, while it should be returning a vApp 653 // Since we don't use this function in terraform-provider-vcd, we are not going to 654 // replace it. 655 return vdc.client.ExecuteTaskRequest(vdcHref.String(), http.MethodPost, 656 types.MimeComposeVappParams, "error instantiating a new vApp: %s", vcomp) 657 } 658 659 // Deprecated: use vdc.GetVAppByName instead 660 func (vdc *Vdc) FindVAppByName(vapp string) (VApp, error) { 661 662 err := vdc.Refresh() 663 if err != nil { 664 return VApp{}, fmt.Errorf("error refreshing vdc: %s", err) 665 } 666 667 for _, resents := range vdc.Vdc.ResourceEntities { 668 for _, resent := range resents.ResourceEntity { 669 670 if resent.Name == vapp && resent.Type == "application/vnd.vmware.vcloud.vApp+xml" { 671 672 newVapp := NewVApp(vdc.client) 673 674 _, err := vdc.client.ExecuteRequest(resent.HREF, http.MethodGet, 675 "", "error retrieving vApp: %s", nil, newVapp.VApp) 676 677 return *newVapp, err 678 679 } 680 } 681 } 682 return VApp{}, fmt.Errorf("can't find vApp: %s", vapp) 683 } 684 685 // Deprecated: use vapp.GetVMByName instead 686 func (vdc *Vdc) FindVMByName(vapp VApp, vm string) (VM, error) { 687 688 err := vdc.Refresh() 689 if err != nil { 690 return VM{}, fmt.Errorf("error refreshing vdc: %s", err) 691 } 692 693 err = vapp.Refresh() 694 if err != nil { 695 return VM{}, fmt.Errorf("error refreshing vApp: %s", err) 696 } 697 698 //vApp Might Not Have Any VMs 699 700 if vapp.VApp.Children == nil { 701 return VM{}, fmt.Errorf("VApp Has No VMs") 702 } 703 704 util.Logger.Printf("[TRACE] Looking for VM: %s", vm) 705 for _, child := range vapp.VApp.Children.VM { 706 707 util.Logger.Printf("[TRACE] Found: %s", child.Name) 708 if child.Name == vm { 709 710 newVm := NewVM(vdc.client) 711 712 _, err := vdc.client.ExecuteRequest(child.HREF, http.MethodGet, 713 "", "error retrieving vm: %s", nil, newVm.VM) 714 715 return *newVm, err 716 } 717 718 } 719 util.Logger.Printf("[TRACE] Couldn't find VM: %s", vm) 720 return VM{}, fmt.Errorf("can't find vm: %s", vm) 721 } 722 723 // Find vm using vApp name and VM name. Returns VMRecord query return type 724 func (vdc *Vdc) QueryVM(vappName, vmName string) (VMRecord, error) { 725 726 if vmName == "" { 727 return VMRecord{}, errors.New("error querying vm name is empty") 728 } 729 730 if vappName == "" { 731 return VMRecord{}, errors.New("error querying vapp name is empty") 732 } 733 734 typeMedia := "vm" 735 if vdc.client.IsSysAdmin { 736 typeMedia = "adminVM" 737 } 738 739 results, err := vdc.QueryWithNotEncodedParams(nil, map[string]string{"type": typeMedia, 740 "filter": "name==" + url.QueryEscape(vmName) + ";containerName==" + url.QueryEscape(vappName), 741 "filterEncoded": "true"}) 742 if err != nil { 743 return VMRecord{}, fmt.Errorf("error querying vm %s", err) 744 } 745 746 vmResults := results.Results.VMRecord 747 if vdc.client.IsSysAdmin { 748 vmResults = results.Results.AdminVMRecord 749 } 750 751 newVM := NewVMRecord(vdc.client) 752 753 if len(vmResults) == 1 { 754 newVM.VM = vmResults[0] 755 } else { 756 return VMRecord{}, fmt.Errorf("found results %d", len(vmResults)) 757 } 758 759 return *newVM, nil 760 } 761 762 // Deprecated: use vdc.GetVAppById instead 763 func (vdc *Vdc) FindVAppByID(vappid string) (VApp, error) { 764 765 // Horrible hack to fetch a vapp with its id. 766 // urn:vcloud:vapp:00000000-0000-0000-0000-000000000000 767 768 err := vdc.Refresh() 769 if err != nil { 770 return VApp{}, fmt.Errorf("error refreshing vdc: %s", err) 771 } 772 773 urnslice := strings.SplitAfter(vappid, ":") 774 urnid := urnslice[len(urnslice)-1] 775 776 for _, resents := range vdc.Vdc.ResourceEntities { 777 for _, resent := range resents.ResourceEntity { 778 779 hrefslice := strings.SplitAfter(resent.HREF, "/") 780 hrefslice = strings.SplitAfter(hrefslice[len(hrefslice)-1], "-") 781 res := strings.Join(hrefslice[1:], "") 782 783 if res == urnid && resent.Type == "application/vnd.vmware.vcloud.vApp+xml" { 784 785 newVapp := NewVApp(vdc.client) 786 787 _, err := vdc.client.ExecuteRequest(resent.HREF, http.MethodGet, 788 "", "error retrieving vApp: %s", nil, newVapp.VApp) 789 790 return *newVapp, err 791 792 } 793 } 794 } 795 return VApp{}, fmt.Errorf("can't find vApp") 796 797 } 798 799 // FindMediaImage returns media image found in system using `name` as query. 800 // Can find a few of them if media with same name exist in different catalogs. 801 // Deprecated: Use catalog.GetMediaByName() 802 func (vdc *Vdc) FindMediaImage(mediaName string) (MediaItem, error) { 803 util.Logger.Printf("[TRACE] Querying medias by name\n") 804 805 mediaResults, err := queryMediaWithFilter(vdc, 806 fmt.Sprintf("name==%s", url.QueryEscape(mediaName))) 807 if err != nil { 808 return MediaItem{}, err 809 } 810 811 newMediaItem := NewMediaItem(vdc) 812 813 if len(mediaResults) == 1 { 814 newMediaItem.MediaItem = mediaResults[0] 815 } 816 817 if len(mediaResults) == 0 { 818 return MediaItem{}, nil 819 } 820 821 if len(mediaResults) > 1 { 822 return MediaItem{}, errors.New("found more than result") 823 } 824 825 util.Logger.Printf("[TRACE] Found media record by name: %#v \n", mediaResults[0]) 826 return *newMediaItem, nil 827 } 828 829 // GetVappByHref returns a vApp reference by running a vCD API call 830 // If no valid vApp is found, it returns a nil VApp reference and an error 831 func (vdc *Vdc) GetVAppByHref(vappHref string) (*VApp, error) { 832 833 newVapp := NewVApp(vdc.client) 834 835 _, err := vdc.client.ExecuteRequest(vappHref, http.MethodGet, 836 "", "error retrieving vApp: %s", nil, newVapp.VApp) 837 838 if err != nil { 839 return nil, err 840 } 841 return newVapp, nil 842 } 843 844 // GetVappByName returns a vApp reference if the vApp Name matches an existing one. 845 // If no valid vApp is found, it returns a nil VApp reference and an error 846 func (vdc *Vdc) GetVAppByName(vappName string, refresh bool) (*VApp, error) { 847 848 if refresh { 849 err := vdc.Refresh() 850 if err != nil { 851 return nil, fmt.Errorf("error refreshing VDC: %s", err) 852 } 853 } 854 855 for _, resourceEntities := range vdc.Vdc.ResourceEntities { 856 for _, resourceReference := range resourceEntities.ResourceEntity { 857 if resourceReference.Name == vappName && resourceReference.Type == "application/vnd.vmware.vcloud.vApp+xml" { 858 return vdc.GetVAppByHref(resourceReference.HREF) 859 } 860 } 861 } 862 return nil, ErrorEntityNotFound 863 } 864 865 // GetVappById returns a vApp reference if the vApp ID matches an existing one. 866 // If no valid vApp is found, it returns a nil VApp reference and an error 867 func (vdc *Vdc) GetVAppById(id string, refresh bool) (*VApp, error) { 868 869 if refresh { 870 err := vdc.Refresh() 871 if err != nil { 872 return nil, fmt.Errorf("error refreshing VDC: %s", err) 873 } 874 } 875 876 for _, resourceEntities := range vdc.Vdc.ResourceEntities { 877 for _, resourceReference := range resourceEntities.ResourceEntity { 878 if equalIds(id, resourceReference.ID, resourceReference.HREF) { 879 return vdc.GetVAppByHref(resourceReference.HREF) 880 } 881 } 882 } 883 return nil, ErrorEntityNotFound 884 } 885 886 // GetVappByNameOrId returns a vApp reference if either the vApp name or ID matches an existing one. 887 // If no valid vApp is found, it returns a nil VApp reference and an error 888 func (vdc *Vdc) GetVAppByNameOrId(identifier string, refresh bool) (*VApp, error) { 889 getByName := func(name string, refresh bool) (interface{}, error) { return vdc.GetVAppByName(name, refresh) } 890 getById := func(id string, refresh bool) (interface{}, error) { return vdc.GetVAppById(id, refresh) } 891 entity, err := getEntityByNameOrId(getByName, getById, identifier, false) 892 if entity == nil { 893 return nil, err 894 } 895 return entity.(*VApp), err 896 } 897 898 // buildNsxvNetworkServiceEndpointURL uses vDC HREF as a base to derive NSX-V based "network 899 // services" endpoint (eg: https://_hostname_or_ip_/network/services + optionalSuffix) 900 func (vdc *Vdc) buildNsxvNetworkServiceEndpointURL(optionalSuffix string) (string, error) { 901 apiEndpoint, err := url.ParseRequestURI(vdc.Vdc.HREF) 902 if err != nil { 903 return "", fmt.Errorf("unable to process vDC URL: %s", err) 904 } 905 906 hostname := apiEndpoint.Scheme + "://" + apiEndpoint.Host + "/network/services" 907 908 if optionalSuffix != "" { 909 return hostname + optionalSuffix, nil 910 } 911 912 return hostname, nil 913 } 914 915 // QueryMediaList retrieves a list of media items for the VDC 916 func (vdc *Vdc) QueryMediaList() ([]*types.MediaRecordType, error) { 917 return getExistingMedia(vdc) 918 } 919 920 // QueryVappVmTemplate Finds VM template using catalog name, vApp template name, VN name in template. 921 // Returns types.QueryResultVMRecordType if it finds the VM. Returns ErrorEntityNotFound 922 // if it's not found. Returns other error if it finds more than one or the search fails. 923 func (vdc *Vdc) QueryVappVmTemplate(catalogName, vappTemplateName, vmNameInTemplate string) (*types.QueryResultVMRecordType, error) { 924 925 queryType := "vm" 926 if vdc.client.IsSysAdmin { 927 queryType = "adminVM" 928 } 929 930 // this allows to query deployed and not deployed templates 931 results, err := vdc.QueryWithNotEncodedParams(nil, map[string]string{"type": queryType, 932 "filter": "catalogName==" + url.QueryEscape(catalogName) + ";containerName==" + url.QueryEscape(vappTemplateName) + ";name==" + url.QueryEscape(vmNameInTemplate) + 933 ";isVAppTemplate==true;status!=FAILED_CREATION;status!=UNKNOWN;status!=UNRECOGNIZED;status!=UNRESOLVED&links=true;", 934 "filterEncoded": "true"}) 935 if err != nil { 936 return nil, fmt.Errorf("error quering all vApp templates: %s", err) 937 } 938 939 vmResults := results.Results.VMRecord 940 if vdc.client.IsSysAdmin { 941 vmResults = results.Results.AdminVMRecord 942 } 943 944 if len(vmResults) == 0 { 945 return nil, fmt.Errorf("[QueryVappVmTemplate] did not find any result with catalog name: %s, "+ 946 "vApp template name: %s, VM name: %s", catalogName, vappTemplateName, vmNameInTemplate) 947 } 948 949 if len(vmResults) > 1 { 950 return nil, fmt.Errorf("[QueryVappVmTemplate] found more than 1 result: %d with with catalog name: %s, "+ 951 "vApp template name: %s, VM name: %s", len(vmResults), catalogName, vappTemplateName, vmNameInTemplate) 952 } 953 954 return vmResults[0], nil 955 } 956 957 // QueryVappSynchronizedVmTemplate Finds a catalog-synchronized VM inside a vApp Template using catalog name, vApp template name, VN name in template. 958 // Returns types.QueryResultVMRecordType if it finds the VM and it's synchronized in the catalog. Returns ErrorEntityNotFound 959 // if it's not found. Returns other error if it finds more than one or the search fails. 960 func (vdc *Vdc) QueryVappSynchronizedVmTemplate(catalogName, vappTemplateName, vmNameInTemplate string) (*types.QueryResultVMRecordType, error) { 961 vmRecord, err := vdc.QueryVappVmTemplate(catalogName, vappTemplateName, vmNameInTemplate) 962 if err != nil { 963 return nil, err 964 } 965 if vmRecord.Status == "LOCAL_COPY_UNAVAILABLE" { 966 return nil, ErrorEntityNotFound 967 } 968 return vmRecord, nil 969 } 970 971 // GetVAppTemplateByName finds a VAppTemplate by Name 972 // On success, returns a pointer to the VAppTemplate structure and a nil error 973 // On failure, returns a nil pointer and an error 974 func (vdc *Vdc) GetVAppTemplateByName(vAppTemplateName string) (*VAppTemplate, error) { 975 vAppTemplateQueryResult, err := vdc.QueryVappTemplateWithName(vAppTemplateName) 976 if err != nil { 977 return nil, err 978 } 979 return getVAppTemplateByHref(vdc.client, vAppTemplateQueryResult.HREF) 980 } 981 982 // GetVAppTemplateByNameOrId finds a vApp Template by Name or ID. 983 // On success, returns a pointer to the VAppTemplate structure and a nil error 984 // On failure, returns a nil pointer and an error 985 func (vdc *Vdc) GetVAppTemplateByNameOrId(identifier string, refresh bool) (*VAppTemplate, error) { 986 getByName := func(name string, refresh bool) (interface{}, error) { return vdc.GetVAppTemplateByName(name) } 987 getById := func(id string, refresh bool) (interface{}, error) { return getVAppTemplateById(vdc.client, id) } 988 entity, err := getEntityByNameOrIdSkipNonId(getByName, getById, identifier, refresh) 989 if entity == nil { 990 return nil, err 991 } 992 return entity.(*VAppTemplate), err 993 } 994 995 // getLinkHref returns a link HREF for a wanted combination of rel and type 996 func (vdc *Vdc) getLinkHref(rel, linkType string) string { 997 for _, link := range vdc.Vdc.Link { 998 if link.Rel == rel && link.Type == linkType { 999 return link.HREF 1000 } 1001 } 1002 return "" 1003 } 1004 1005 // GetVappList returns the list of vApps for a VDC 1006 func (vdc *Vdc) GetVappList() []*types.ResourceReference { 1007 var list []*types.ResourceReference 1008 for _, resourceEntities := range vdc.Vdc.ResourceEntities { 1009 for _, resourceReference := range resourceEntities.ResourceEntity { 1010 if resourceReference.Type == types.MimeVApp { 1011 list = append(list, resourceReference) 1012 } 1013 } 1014 } 1015 return list 1016 } 1017 1018 // CreateStandaloneVmAsync starts a standalone VM creation without a template, returning a task 1019 func (vdc *Vdc) CreateStandaloneVmAsync(params *types.CreateVmParams) (Task, error) { 1020 util.Logger.Printf("[TRACE] Vdc.CreateStandaloneVmAsync - Creating VM ") 1021 1022 if vdc.Vdc.HREF == "" { 1023 return Task{}, fmt.Errorf("cannot create VM, Object VDC is empty") 1024 } 1025 1026 href := "" 1027 for _, link := range vdc.Vdc.Link { 1028 if link.Type == types.MimeCreateVmParams && link.Rel == "add" { 1029 href = link.HREF 1030 break 1031 } 1032 } 1033 if href == "" { 1034 return Task{}, fmt.Errorf("error retrieving VM creation link from VDC %s", vdc.Vdc.Name) 1035 } 1036 if params == nil { 1037 return Task{}, fmt.Errorf("empty parameters passed to standalone VM creation") 1038 } 1039 params.XmlnsOvf = types.XMLNamespaceOVF 1040 1041 // 37.1 Introduced new parameters to VM configuration 1042 return vdc.client.ExecuteTaskRequestWithApiVersion(href, http.MethodPost, 1043 types.MimeCreateVmParams, "error creating standalone VM: %s", params, 1044 vdc.client.GetSpecificApiVersionOnCondition(">=37.1", "37.1")) 1045 } 1046 1047 // getVmFromTask finds a VM from a running standalone VM creation task 1048 // It retrieves the VM owner (the hidden vApp), and from that one finds the new VM 1049 func (vdc *Vdc) getVmFromTask(task Task, name string) (*VM, error) { 1050 owner := task.Task.Owner.HREF 1051 if owner == "" { 1052 return nil, fmt.Errorf("task owner is null for VM %s", name) 1053 } 1054 vapp, err := vdc.GetVAppByHref(owner) 1055 if err != nil { 1056 return nil, err 1057 } 1058 if vapp.VApp.Children == nil { 1059 return nil, ErrorEntityNotFound 1060 } 1061 if len(vapp.VApp.Children.VM) == 0 { 1062 return nil, fmt.Errorf("vApp %s contains no VMs", vapp.VApp.Name) 1063 } 1064 if len(vapp.VApp.Children.VM) > 1 { 1065 return nil, fmt.Errorf("vApp %s contains more than one VM", vapp.VApp.Name) 1066 } 1067 for _, child := range vapp.VApp.Children.VM { 1068 util.Logger.Printf("[TRACE] Looking at: %s", child.Name) 1069 return vapp.client.GetVMByHref(child.HREF) 1070 } 1071 return nil, ErrorEntityNotFound 1072 } 1073 1074 // CreateStandaloneVm creates a standalone VM without a template 1075 func (vdc *Vdc) CreateStandaloneVm(params *types.CreateVmParams) (*VM, error) { 1076 1077 task, err := vdc.CreateStandaloneVmAsync(params) 1078 if err != nil { 1079 return nil, err 1080 } 1081 err = task.WaitTaskCompletion() 1082 if err != nil { 1083 return nil, err 1084 } 1085 return vdc.getVmFromTask(task, params.Name) 1086 } 1087 1088 // QueryVmByName finds a standalone VM by name 1089 // The search fails either if there are more VMs with the wanted name, or if there are none 1090 // It can also retrieve a standard VM (created from vApp) 1091 func (vdc *Vdc) QueryVmByName(name string) (*VM, error) { 1092 vmList, err := vdc.QueryVmList(types.VmQueryFilterOnlyDeployed) 1093 if err != nil { 1094 return nil, err 1095 } 1096 var foundVM []*types.QueryResultVMRecordType 1097 for _, vm := range vmList { 1098 if vm.Name == name { 1099 foundVM = append(foundVM, vm) 1100 } 1101 } 1102 if len(foundVM) == 0 { 1103 return nil, ErrorEntityNotFound 1104 } 1105 if len(foundVM) > 1 { 1106 return nil, fmt.Errorf("more than one VM found with name %s", name) 1107 } 1108 return vdc.client.GetVMByHref(foundVM[0].HREF) 1109 } 1110 1111 // QueryVmById retrieves a standalone VM by ID in an Org 1112 // It can also retrieve a standard VM (created from vApp) 1113 func (org *Org) QueryVmById(id string) (*VM, error) { 1114 return queryVmById(id, org.client, org.QueryVmList) 1115 } 1116 1117 // QueryVmById retrieves a standalone VM by ID in a Vdc 1118 // It can also retrieve a standard VM (created from vApp) 1119 func (vdc *Vdc) QueryVmById(id string) (*VM, error) { 1120 return queryVmById(id, vdc.client, vdc.QueryVmList) 1121 } 1122 1123 // queryVmListFunc 1124 type queryVmListFunc func(filter types.VmQueryFilter) ([]*types.QueryResultVMRecordType, error) 1125 1126 // queryVmById is shared between org.QueryVmById and vdc.QueryVmById which allow to search for VM 1127 // in different scope (Org or VDC) 1128 func queryVmById(id string, client *Client, queryFunc queryVmListFunc) (*VM, error) { 1129 vmList, err := queryFunc(types.VmQueryFilterOnlyDeployed) 1130 if err != nil { 1131 return nil, err 1132 } 1133 var foundVM []*types.QueryResultVMRecordType 1134 for _, vm := range vmList { 1135 if equalIds(id, vm.ID, vm.HREF) { 1136 foundVM = append(foundVM, vm) 1137 } 1138 } 1139 if len(foundVM) == 0 { 1140 return nil, ErrorEntityNotFound 1141 } 1142 if len(foundVM) > 1 { 1143 return nil, fmt.Errorf("more than one VM found with ID %s", id) 1144 } 1145 return client.GetVMByHref(foundVM[0].HREF) 1146 } 1147 1148 // CreateStandaloneVMFromTemplateAsync starts a standalone VM creation using a template 1149 func (vdc *Vdc) CreateStandaloneVMFromTemplateAsync(params *types.InstantiateVmTemplateParams) (Task, error) { 1150 1151 util.Logger.Printf("[TRACE] Vdc.CreateStandaloneVMFromTemplateAsync - Creating VM") 1152 1153 if vdc.Vdc.HREF == "" { 1154 return Task{}, fmt.Errorf("cannot create VM, provided VDC is empty") 1155 } 1156 1157 href := "" 1158 for _, link := range vdc.Vdc.Link { 1159 if link.Type == types.MimeInstantiateVmTemplateParams && link.Rel == "add" { 1160 href = link.HREF 1161 break 1162 } 1163 } 1164 if href == "" { 1165 return Task{}, fmt.Errorf("error retrieving VM instantiate from template link from VDC %s", vdc.Vdc.Name) 1166 } 1167 1168 if params.Name == "" { 1169 return Task{}, fmt.Errorf("[CreateStandaloneVMFromTemplateAsync] missing VM name") 1170 } 1171 if params.SourcedVmTemplateItem == nil { 1172 return Task{}, fmt.Errorf("[CreateStandaloneVMFromTemplateAsync] missing SourcedVmTemplateItem") 1173 } 1174 if params.SourcedVmTemplateItem.Source == nil { 1175 return Task{}, fmt.Errorf("[CreateStandaloneVMFromTemplateAsync] missing vApp template Source") 1176 } 1177 if params.SourcedVmTemplateItem.Source.HREF == "" { 1178 return Task{}, fmt.Errorf("[CreateStandaloneVMFromTemplateAsync] empty HREF in vApp template Source") 1179 } 1180 params.XmlnsOvf = types.XMLNamespaceOVF 1181 1182 return vdc.client.ExecuteTaskRequest(href, http.MethodPost, types.MimeInstantiateVmTemplateParams, "error creating standalone VM from template: %s", params) 1183 } 1184 1185 // CreateStandaloneVMFromTemplate creates a standalone VM from a template 1186 func (vdc *Vdc) CreateStandaloneVMFromTemplate(params *types.InstantiateVmTemplateParams) (*VM, error) { 1187 1188 task, err := vdc.CreateStandaloneVMFromTemplateAsync(params) 1189 if err != nil { 1190 return nil, err 1191 } 1192 err = task.WaitTaskCompletion() 1193 if err != nil { 1194 return nil, err 1195 } 1196 return vdc.getVmFromTask(task, params.Name) 1197 } 1198 1199 // GetCapabilities allows to retrieve a list of VDC capabilities. It has a list of values. Some particularly useful are: 1200 // * networkProvider - overlay stack responsible for providing network functionality. (NSX_V or NSX_T) 1201 // * crossVdc - supports cross vDC network creation 1202 func (vdc *Vdc) GetCapabilities() ([]types.VdcCapability, error) { 1203 if vdc.Vdc.ID == "" { 1204 return nil, fmt.Errorf("VDC ID must be set to get capabilities") 1205 } 1206 1207 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointVdcCapabilities 1208 minimumApiVersion, err := vdc.client.checkOpenApiEndpointCompatibility(endpoint) 1209 if err != nil { 1210 return nil, err 1211 } 1212 1213 urlRef, err := vdc.client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, url.QueryEscape(vdc.Vdc.ID))) 1214 if err != nil { 1215 return nil, err 1216 } 1217 1218 capabilities := make([]types.VdcCapability, 0) 1219 err = vdc.client.OpenApiGetAllItems(minimumApiVersion, urlRef, nil, &capabilities, nil) 1220 if err != nil { 1221 return nil, err 1222 } 1223 return capabilities, nil 1224 } 1225 1226 // IsNsxt is a convenience function to check if VDC is backed by NSX-T pVdc 1227 // If error occurs - it returns false 1228 func (vdc *Vdc) IsNsxt() bool { 1229 vdcCapabilities, err := vdc.GetCapabilities() 1230 if err != nil { 1231 return false 1232 } 1233 1234 networkProviderCapability := getCapabilityValue(vdcCapabilities, "networkProvider") 1235 return networkProviderCapability == types.VdcCapabilityNetworkProviderNsxt 1236 } 1237 1238 // IsNsxv is a convenience function to check if VDC is backed by NSX-V pVdc 1239 // If error occurs - it returns false 1240 func (vdc *Vdc) IsNsxv() bool { 1241 vdcCapabilities, err := vdc.GetCapabilities() 1242 if err != nil { 1243 return false 1244 } 1245 1246 networkProviderCapability := getCapabilityValue(vdcCapabilities, "networkProvider") 1247 return networkProviderCapability == types.VdcCapabilityNetworkProviderNsxv 1248 } 1249 1250 // getCapabilityValue helps to lookup a specific capability in []types.VdcCapability by provided fieldName 1251 func getCapabilityValue(capabilities []types.VdcCapability, fieldName string) string { 1252 for _, field := range capabilities { 1253 if field.Name == fieldName { 1254 return field.Value.(string) 1255 } 1256 } 1257 1258 return "" 1259 } 1260 1261 func (vdc *Vdc) getParentOrg() (organization, error) { 1262 for _, vdcLink := range vdc.Vdc.Link { 1263 if vdcLink.Rel != "up" { 1264 continue 1265 } 1266 switch vdcLink.Type { 1267 case types.MimeOrg: 1268 org, err := getOrgByHref(vdc.client, vdcLink.HREF) 1269 if err != nil { 1270 return nil, err 1271 } 1272 return org, nil 1273 case types.MimeAdminOrg: 1274 adminOrg, err := getAdminOrgByHref(vdc.client, vdcLink.HREF) 1275 if err != nil { 1276 return nil, err 1277 } 1278 return adminOrg, nil 1279 1280 default: 1281 continue 1282 } 1283 } 1284 return nil, fmt.Errorf("no parent found for VDC %s", vdc.Vdc.Name) 1285 } 1286 1287 // CreateVappFromTemplate instantiates a new vApp from a vApp template 1288 // The template argument must contain at least: 1289 // * Name 1290 // * Source (a reference to the source vApp template) 1291 func (vdc *Vdc) CreateVappFromTemplate(template *types.InstantiateVAppTemplateParams) (*VApp, error) { 1292 vdcHref, err := url.ParseRequestURI(vdc.Vdc.HREF) 1293 if err != nil { 1294 return nil, fmt.Errorf("error getting VDC href: %s", err) 1295 } 1296 vdcHref.Path += "/action/instantiateVAppTemplate" 1297 1298 vapp := NewVApp(vdc.client) 1299 1300 template.Xmlns = types.XMLNamespaceVCloud 1301 template.Ovf = types.XMLNamespaceOVF 1302 template.Deploy = true 1303 1304 _, err = vdc.client.ExecuteRequest(vdcHref.String(), http.MethodPost, 1305 types.MimeInstantiateVappTemplateParams, "error instantiating a new vApp from Template: %s", template, vapp.VApp) 1306 if err != nil { 1307 return nil, err 1308 } 1309 1310 task := NewTask(vdc.client) 1311 for _, taskItem := range vapp.VApp.Tasks.Task { 1312 task.Task = taskItem 1313 err = task.WaitTaskCompletion() 1314 if err != nil { 1315 return nil, fmt.Errorf("error performing task: %s", err) 1316 } 1317 } 1318 err = vapp.Refresh() 1319 return vapp, err 1320 } 1321 1322 // CloneVapp makes a copy of a vApp into a new one 1323 // The sourceVapp argument must contain at least: 1324 // * Name 1325 // * Source (a reference to the source vApp) 1326 func (vdc *Vdc) CloneVapp(sourceVapp *types.CloneVAppParams) (*VApp, error) { 1327 vdcHref, err := url.ParseRequestURI(vdc.Vdc.HREF) 1328 if err != nil { 1329 return nil, fmt.Errorf("error getting VDC href: %s", err) 1330 } 1331 vdcHref.Path += "/action/cloneVApp" 1332 1333 vapp := NewVApp(vdc.client) 1334 1335 sourceVapp.Xmlns = types.XMLNamespaceVCloud 1336 sourceVapp.Ovf = types.XMLNamespaceOVF 1337 sourceVapp.Deploy = true 1338 1339 _, err = vdc.client.ExecuteRequest(vdcHref.String(), http.MethodPost, 1340 types.MimeCloneVapp, "error cloning a vApp : %s", sourceVapp, vapp.VApp) 1341 if err != nil { 1342 return nil, err 1343 } 1344 1345 task := NewTask(vdc.client) 1346 for _, taskItem := range vapp.VApp.Tasks.Task { 1347 task.Task = taskItem 1348 err = task.WaitTaskCompletion() 1349 if err != nil { 1350 return nil, fmt.Errorf("error performing task: %s", err) 1351 } 1352 } 1353 err = vapp.Refresh() 1354 return vapp, err 1355 } 1356 1357 // Get the details of a hardware version 1358 func (vdc *Vdc) GetHardwareVersion(name string) (*types.VirtualHardwareVersion, error) { 1359 if len(vdc.Vdc.Capabilities) == 0 { 1360 return nil, fmt.Errorf("VDC doesn't have any virtual hardware version support information stored") 1361 } 1362 1363 found := false 1364 for _, hwVersion := range vdc.Vdc.Capabilities[0].SupportedHardwareVersions.SupportedHardwareVersion { 1365 if hwVersion.Name == name { 1366 found = true 1367 } 1368 } 1369 if !found { 1370 return nil, fmt.Errorf("hardware version %s not found or not supported", name) 1371 } 1372 1373 vdcHref, err := url.ParseRequestURI(vdc.Vdc.HREF) 1374 if err != nil { 1375 return nil, fmt.Errorf("error getting VDC href: %s", err) 1376 } 1377 vdcHref.Path += "/hwv/" + name 1378 1379 hardwareVersion := &types.VirtualHardwareVersion{} 1380 1381 _, err = vdc.client.ExecuteRequest(vdcHref.String(), http.MethodGet, types.MimeVirtualHardwareVersion, "error getting hardware version: %s", nil, hardwareVersion) 1382 if err != nil { 1383 return nil, err 1384 } 1385 1386 return hardwareVersion, nil 1387 } 1388 1389 // Get highest supported hardware version of a VDC 1390 func (vdc *Vdc) GetHighestHardwareVersion() (*types.VirtualHardwareVersion, error) { 1391 err := vdc.Refresh() 1392 if err != nil { 1393 return nil, err 1394 } 1395 1396 if len(vdc.Vdc.Capabilities) == 0 { 1397 return nil, fmt.Errorf("VDC doesn't have any virtual hardware version support information stored") 1398 } 1399 1400 hardwareVersions := vdc.Vdc.Capabilities[0].SupportedHardwareVersions.SupportedHardwareVersion 1401 // Get last item (highest version) of SupportedHardwareVersions 1402 highestVersion := hardwareVersions[len(hardwareVersions)-1].Name 1403 1404 hardwareVersion, err := vdc.GetHardwareVersion(highestVersion) 1405 if err != nil { 1406 return nil, err 1407 } 1408 return hardwareVersion, nil 1409 } 1410 1411 // FindOsFromId attempts to find a OS by ID using the given hardware version 1412 func (vdc *Vdc) FindOsFromId(hardwareVersion *types.VirtualHardwareVersion, osId string) (*types.OperatingSystemInfoType, error) { 1413 for _, osFamily := range hardwareVersion.SupportedOperatingSystems.OperatingSystemFamilyInfo { 1414 for _, os := range osFamily.OperatingSystems { 1415 if osId == os.InternalName { 1416 return os, nil 1417 } 1418 } 1419 } 1420 1421 return nil, fmt.Errorf("no OS found with ID: %s", osId) 1422 }