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  }