github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/vapp.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  	"encoding/xml"
     9  	"errors"
    10  	"fmt"
    11  	"net/http"
    12  	"strconv"
    13  	"time"
    14  
    15  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    16  	"github.com/vmware/go-vcloud-director/v2/util"
    17  )
    18  
    19  type VApp struct {
    20  	VApp   *types.VApp
    21  	client *Client
    22  }
    23  
    24  func NewVApp(cli *Client) *VApp {
    25  	return &VApp{
    26  		VApp:   new(types.VApp),
    27  		client: cli,
    28  	}
    29  }
    30  
    31  func (vcdClient *VCDClient) NewVApp(client *Client) VApp {
    32  	newvapp := NewVApp(client)
    33  	return *newvapp
    34  }
    35  
    36  // struct type used to pass information for vApp network creation
    37  type VappNetworkSettings struct {
    38  	ID                 string
    39  	Name               string
    40  	Description        string
    41  	Gateway            string
    42  	NetMask            string
    43  	SubnetPrefixLength string
    44  	DNS1               string
    45  	DNS2               string
    46  	DNSSuffix          string
    47  	GuestVLANAllowed   *bool
    48  	StaticIPRanges     []*types.IPRange
    49  	DhcpSettings       *DhcpSettings
    50  	RetainIpMacEnabled *bool
    51  	VappFenceEnabled   *bool
    52  }
    53  
    54  // struct type used to pass information for vApp network DHCP
    55  type DhcpSettings struct {
    56  	IsEnabled        bool
    57  	MaxLeaseTime     int
    58  	DefaultLeaseTime int
    59  	IPRange          *types.IPRange
    60  }
    61  
    62  // Returns the vdc where the vapp resides in.
    63  func (vapp *VApp) GetParentVDC() (Vdc, error) {
    64  	for _, link := range vapp.VApp.Link {
    65  		if (link.Type == types.MimeVDC || link.Type == types.MimeAdminVDC) && link.Rel == "up" {
    66  
    67  			vdc := NewVdc(vapp.client)
    68  
    69  			_, err := vapp.client.ExecuteRequest(link.HREF, http.MethodGet,
    70  				"", "error retrieving parent vdc: %s", nil, vdc.Vdc)
    71  			if err != nil {
    72  				return Vdc{}, err
    73  			}
    74  
    75  			parent, err := vdc.getParentOrg()
    76  			if err != nil {
    77  				return Vdc{}, err
    78  			}
    79  			vdc.parent = parent
    80  			return *vdc, nil
    81  		}
    82  	}
    83  	return Vdc{}, fmt.Errorf("could not find a parent Vdc")
    84  }
    85  
    86  func (vapp *VApp) Refresh() error {
    87  
    88  	if vapp.VApp.HREF == "" {
    89  		return fmt.Errorf("cannot refresh, Object is empty")
    90  	}
    91  
    92  	url := vapp.VApp.HREF
    93  	// Empty struct before a new unmarshal, otherwise we end up with duplicate
    94  	// elements in slices.
    95  	vapp.VApp = &types.VApp{}
    96  
    97  	_, err := vapp.client.ExecuteRequest(url, http.MethodGet,
    98  		"", "error refreshing vApp: %s", nil, vapp.VApp)
    99  
   100  	// The request was successful
   101  	return err
   102  }
   103  
   104  // AddVM create vm in vApp using vApp template
   105  // orgVdcNetworks - adds org VDC networks to be available for vApp. Can be empty.
   106  // vappNetworkName - adds vApp network to be available for vApp. Can be empty.
   107  // vappTemplate - vApp Template which will be used for VM creation.
   108  // name - name for VM.
   109  // acceptAllEulas - setting allows to automatically accept or not Eulas.
   110  //
   111  // Deprecated: Use vapp.AddNewVM instead for more sophisticated network handling
   112  func (vapp *VApp) AddVM(orgVdcNetworks []*types.OrgVDCNetwork, vappNetworkName string, vappTemplate VAppTemplate, name string, acceptAllEulas bool) (Task, error) {
   113  	util.Logger.Printf("[INFO] vapp.AddVM() is deprecated in favor of vapp.AddNewVM()")
   114  	if vappTemplate == (VAppTemplate{}) || vappTemplate.VAppTemplate == nil {
   115  		return Task{}, fmt.Errorf("vApp Template can not be empty")
   116  	}
   117  
   118  	// primaryNetworkConnectionIndex will be inherited from template or defaulted to 0
   119  	// if the template does not have any NICs assigned.
   120  	primaryNetworkConnectionIndex := 0
   121  	if vappTemplate.VAppTemplate.Children != nil && len(vappTemplate.VAppTemplate.Children.VM) > 0 &&
   122  		vappTemplate.VAppTemplate.Children.VM[0].NetworkConnectionSection != nil {
   123  		primaryNetworkConnectionIndex = vappTemplate.VAppTemplate.Children.VM[0].NetworkConnectionSection.PrimaryNetworkConnectionIndex
   124  	}
   125  
   126  	networkConnectionSection := types.NetworkConnectionSection{
   127  		Info:                          "Network config for sourced item",
   128  		PrimaryNetworkConnectionIndex: primaryNetworkConnectionIndex,
   129  	}
   130  
   131  	for index, orgVdcNetwork := range orgVdcNetworks {
   132  		networkConnectionSection.NetworkConnection = append(networkConnectionSection.NetworkConnection,
   133  			&types.NetworkConnection{
   134  				Network:                 orgVdcNetwork.Name,
   135  				NetworkConnectionIndex:  index,
   136  				IsConnected:             true,
   137  				IPAddressAllocationMode: types.IPAllocationModePool,
   138  			},
   139  		)
   140  	}
   141  
   142  	if vappNetworkName != "" {
   143  		networkConnectionSection.NetworkConnection = append(networkConnectionSection.NetworkConnection,
   144  			&types.NetworkConnection{
   145  				Network:                 vappNetworkName,
   146  				NetworkConnectionIndex:  len(orgVdcNetworks),
   147  				IsConnected:             true,
   148  				IPAddressAllocationMode: types.IPAllocationModePool,
   149  			},
   150  		)
   151  	}
   152  
   153  	return vapp.AddNewVM(name, vappTemplate, &networkConnectionSection, acceptAllEulas)
   154  }
   155  
   156  // AddRawVM accepts raw types.ReComposeVAppParams which contains all information for VM creation
   157  func (vapp *VApp) AddRawVM(vAppComposition *types.ReComposeVAppParams) (*VM, error) {
   158  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
   159  	apiEndpoint.Path += "/action/recomposeVApp"
   160  
   161  	// Return the task
   162  	task, err := vapp.client.ExecuteTaskRequestWithApiVersion(apiEndpoint.String(), http.MethodPost,
   163  		types.MimeRecomposeVappParams, "error instantiating a new VM: %s",
   164  		vAppComposition, vapp.client.GetSpecificApiVersionOnCondition(">=37.1", "37.1"))
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	err = task.WaitTaskCompletion()
   170  	if err != nil {
   171  		return nil, fmt.Errorf("VM creation task failed: %s", err)
   172  	}
   173  
   174  	// VM task does not return any reference to VM therefore it must be looked up by name after
   175  	// creation
   176  
   177  	var vmName string
   178  	if vAppComposition.SourcedItem != nil && vAppComposition.SourcedItem.Source != nil {
   179  		vmName = vAppComposition.SourcedItem.Source.Name
   180  	}
   181  
   182  	vm, err := vapp.GetVMByName(vmName, true)
   183  	if err != nil {
   184  		return nil, fmt.Errorf("error finding VM %s in vApp %s after creation: %s", vAppComposition.Name, vapp.VApp.Name, err)
   185  	}
   186  
   187  	return vm, nil
   188  
   189  }
   190  
   191  // AddNewVM adds VM from vApp template with custom NetworkConnectionSection
   192  func (vapp *VApp) AddNewVM(name string, vappTemplate VAppTemplate, network *types.NetworkConnectionSection, acceptAllEulas bool) (Task, error) {
   193  	return vapp.AddNewVMWithStorageProfile(name, vappTemplate, network, nil, acceptAllEulas)
   194  }
   195  
   196  // AddNewVMWithStorageProfile adds VM from vApp template with custom NetworkConnectionSection and optional storage profile
   197  func (vapp *VApp) AddNewVMWithStorageProfile(name string, vappTemplate VAppTemplate,
   198  	network *types.NetworkConnectionSection,
   199  	storageProfileRef *types.Reference, acceptAllEulas bool) (Task, error) {
   200  	return addNewVMW(vapp, name, vappTemplate, network, storageProfileRef, nil, acceptAllEulas)
   201  }
   202  
   203  // AddNewVMWithComputePolicy adds VM from vApp template with custom NetworkConnectionSection and optional storage profile
   204  // and compute policy
   205  func (vapp *VApp) AddNewVMWithComputePolicy(name string, vappTemplate VAppTemplate,
   206  	network *types.NetworkConnectionSection,
   207  	storageProfileRef *types.Reference, computePolicy *types.VdcComputePolicy, acceptAllEulas bool) (Task, error) {
   208  	return addNewVMW(vapp, name, vappTemplate, network, storageProfileRef, computePolicy, acceptAllEulas)
   209  }
   210  
   211  // addNewVMW adds VM from vApp template with custom NetworkConnectionSection and optional storage profile
   212  // and optional compute policy
   213  func addNewVMW(vapp *VApp, name string, vappTemplate VAppTemplate,
   214  	network *types.NetworkConnectionSection,
   215  	storageProfileRef *types.Reference, computePolicy *types.VdcComputePolicy, acceptAllEulas bool) (Task, error) {
   216  
   217  	if vappTemplate == (VAppTemplate{}) || vappTemplate.VAppTemplate == nil {
   218  		return Task{}, fmt.Errorf("vApp Template can not be empty")
   219  	}
   220  
   221  	templateHref := vappTemplate.VAppTemplate.HREF
   222  	if vappTemplate.VAppTemplate.Children != nil && len(vappTemplate.VAppTemplate.Children.VM) != 0 {
   223  		templateHref = vappTemplate.VAppTemplate.Children.VM[0].HREF
   224  	}
   225  
   226  	// Status 8 means The object is resolved and powered off.
   227  	// https://vdc-repo.vmware.com/vmwb-repository/dcr-public/94b8bd8d-74ff-4fe3-b7a4-41ae31516ed7/1b42f3b5-8b31-4279-8b3f-547f6c7c5aa8/doc/GUID-843BE3AD-5EF6-4442-B864-BCAE44A51867.html
   228  	if vappTemplate.VAppTemplate.Status != 8 {
   229  		return Task{}, fmt.Errorf("vApp Template shape is not ok (status: %d)", vappTemplate.VAppTemplate.Status)
   230  	}
   231  
   232  	// Validate network config only if it was supplied
   233  	if network != nil && network.NetworkConnection != nil {
   234  		for _, nic := range network.NetworkConnection {
   235  			if nic.Network == "" {
   236  				return Task{}, fmt.Errorf("missing mandatory attribute Network: %s", nic.Network)
   237  			}
   238  			if nic.IPAddressAllocationMode == "" {
   239  				return Task{}, fmt.Errorf("missing mandatory attribute IPAddressAllocationMode: %s", nic.IPAddressAllocationMode)
   240  			}
   241  		}
   242  	}
   243  
   244  	vAppComposition := &types.ReComposeVAppParams{
   245  		Ovf:         types.XMLNamespaceOVF,
   246  		Xsi:         types.XMLNamespaceXSI,
   247  		Xmlns:       types.XMLNamespaceVCloud,
   248  		Deploy:      false,
   249  		Name:        vapp.VApp.Name,
   250  		PowerOn:     false,
   251  		Description: vapp.VApp.Description,
   252  		SourcedItem: &types.SourcedCompositionItemParam{
   253  			Source: &types.Reference{
   254  				HREF: templateHref,
   255  				Name: name,
   256  			},
   257  			InstantiationParams: &types.InstantiationParams{}, // network config is injected below
   258  		},
   259  		AllEULAsAccepted: acceptAllEulas,
   260  	}
   261  
   262  	// Add storage profile
   263  	if storageProfileRef != nil && storageProfileRef.HREF != "" {
   264  		vAppComposition.SourcedItem.StorageProfile = storageProfileRef
   265  	}
   266  
   267  	// Add compute policy
   268  	if computePolicy != nil && computePolicy.ID != "" {
   269  		vdcComputePolicyHref, err := vapp.client.OpenApiBuildEndpoint(types.OpenApiPathVersion1_0_0, types.OpenApiEndpointVdcComputePolicies, computePolicy.ID)
   270  		if err != nil {
   271  			return Task{}, fmt.Errorf("error constructing HREF for compute policy")
   272  		}
   273  		vAppComposition.SourcedItem.ComputePolicy = &types.ComputePolicy{VmSizingPolicy: &types.Reference{HREF: vdcComputePolicyHref.String()}}
   274  	}
   275  
   276  	// Inject network config
   277  	vAppComposition.SourcedItem.InstantiationParams.NetworkConnectionSection = network
   278  
   279  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
   280  	apiEndpoint.Path += "/action/recomposeVApp"
   281  
   282  	// Return the task
   283  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost,
   284  		types.MimeRecomposeVappParams, "error instantiating a new VM: %s", vAppComposition)
   285  
   286  }
   287  
   288  // ========================= issue#252 ==================================
   289  // TODO: To be refactored, handling networks better. See issue#252 for details
   290  // https://github.com/vmware/go-vcloud-director/issues/252
   291  // ======================================================================
   292  func (vapp *VApp) RemoveVM(vm VM) error {
   293  	err := vapp.Refresh()
   294  	if err != nil {
   295  		return fmt.Errorf("error refreshing vApp before removing VM: %s", err)
   296  	}
   297  	task := NewTask(vapp.client)
   298  	if vapp.VApp.Tasks != nil {
   299  		for _, taskItem := range vapp.VApp.Tasks.Task {
   300  			task.Task = taskItem
   301  			// Leftover tasks may have unhandled errors that can be dismissed at this stage
   302  			// we complete any incomplete tasks at this stage, to finish the refresh.
   303  			if task.Task.Status != "error" && task.Task.Status != "success" {
   304  				err := task.WaitTaskCompletion()
   305  				if err != nil {
   306  					return fmt.Errorf("error performing task: %s", err)
   307  				}
   308  			}
   309  		}
   310  	}
   311  
   312  	vcomp := &types.ReComposeVAppParams{
   313  		Ovf:   types.XMLNamespaceOVF,
   314  		Xsi:   types.XMLNamespaceXSI,
   315  		Xmlns: types.XMLNamespaceVCloud,
   316  		DeleteItem: &types.DeleteItem{
   317  			HREF: vm.VM.HREF,
   318  		},
   319  	}
   320  
   321  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
   322  	apiEndpoint.Path += "/action/recomposeVApp"
   323  
   324  	deleteTask, err := vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost,
   325  		types.MimeRecomposeVappParams, "error removing VM: %s", vcomp)
   326  	if err != nil {
   327  		return err
   328  	}
   329  
   330  	err = deleteTask.WaitTaskCompletion()
   331  	if err != nil {
   332  		return fmt.Errorf("error performing removing VM task: %s", err)
   333  	}
   334  
   335  	return nil
   336  }
   337  
   338  func (vapp *VApp) PowerOn() (Task, error) {
   339  
   340  	err := vapp.BlockWhileStatus("UNRESOLVED", vapp.client.MaxRetryTimeout)
   341  	if err != nil {
   342  		return Task{}, fmt.Errorf("error powering on vApp: %s", err)
   343  	}
   344  
   345  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
   346  	apiEndpoint.Path += "/power/action/powerOn"
   347  
   348  	// Return the task
   349  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost,
   350  		"", "error powering on vApp: %s", nil)
   351  }
   352  
   353  func (vapp *VApp) PowerOff() (Task, error) {
   354  
   355  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
   356  	apiEndpoint.Path += "/power/action/powerOff"
   357  
   358  	// Return the task
   359  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost,
   360  		"", "error powering off vApp: %s", nil)
   361  
   362  }
   363  
   364  func (vapp *VApp) Reboot() (Task, error) {
   365  
   366  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
   367  	apiEndpoint.Path += "/power/action/reboot"
   368  
   369  	// Return the task
   370  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost,
   371  		"", "error rebooting vApp: %s", nil)
   372  }
   373  
   374  func (vapp *VApp) Reset() (Task, error) {
   375  
   376  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
   377  	apiEndpoint.Path += "/power/action/reset"
   378  
   379  	// Return the task
   380  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost,
   381  		"", "error resetting vApp: %s", nil)
   382  }
   383  
   384  // Suspend suspends a vApp
   385  func (vapp *VApp) Suspend() (Task, error) {
   386  
   387  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
   388  	apiEndpoint.Path += "/power/action/suspend"
   389  
   390  	// Return the task
   391  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost,
   392  		"", "error suspending vApp: %s", nil)
   393  }
   394  
   395  // DiscardSuspendedState takes back a vApp from suspension
   396  func (vapp *VApp) DiscardSuspendedState() error {
   397  	// Status 3 means that the vApp is suspended
   398  	if vapp.VApp.Status != 3 {
   399  		return nil
   400  	}
   401  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
   402  	apiEndpoint.Path += "/action/discardSuspendedState"
   403  
   404  	// Return the task
   405  	task, err := vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost,
   406  		"", "error discarding suspended state for vApp: %s", nil)
   407  	if err != nil {
   408  		return err
   409  	}
   410  	return task.WaitTaskCompletion()
   411  }
   412  
   413  func (vapp *VApp) Shutdown() (Task, error) {
   414  
   415  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
   416  	apiEndpoint.Path += "/power/action/shutdown"
   417  
   418  	// Return the task
   419  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost,
   420  		"", "error shutting down vApp: %s", nil)
   421  }
   422  
   423  func (vapp *VApp) Undeploy() (Task, error) {
   424  
   425  	vu := &types.UndeployVAppParams{
   426  		Xmlns:               types.XMLNamespaceVCloud,
   427  		UndeployPowerAction: "powerOff",
   428  	}
   429  
   430  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
   431  	apiEndpoint.Path += "/action/undeploy"
   432  
   433  	// Return the task
   434  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost,
   435  		types.MimeUndeployVappParams, "error undeploy vApp: %s", vu)
   436  }
   437  
   438  func (vapp *VApp) Deploy() (Task, error) {
   439  
   440  	vu := &types.DeployVAppParams{
   441  		Xmlns:   types.XMLNamespaceVCloud,
   442  		PowerOn: false,
   443  	}
   444  
   445  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
   446  	apiEndpoint.Path += "/action/deploy"
   447  
   448  	// Return the task
   449  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost,
   450  		types.MimeDeployVappParams, "error deploy vApp: %s", vu)
   451  }
   452  
   453  func (vapp *VApp) Delete() (Task, error) {
   454  
   455  	// Return the task
   456  	return vapp.client.ExecuteTaskRequest(vapp.VApp.HREF, http.MethodDelete,
   457  		"", "error deleting vApp: %s", nil)
   458  }
   459  
   460  func (vapp *VApp) RunCustomizationScript(computername, script string) (Task, error) {
   461  	return vapp.Customize(computername, script, false)
   462  }
   463  
   464  // Customize applies customization to first child VM
   465  //
   466  // Deprecated: Use vm.SetGuestCustomizationSection()
   467  func (vapp *VApp) Customize(computername, script string, changeSid bool) (Task, error) {
   468  	err := vapp.Refresh()
   469  	if err != nil {
   470  		return Task{}, fmt.Errorf("error refreshing vApp before running customization: %s", err)
   471  	}
   472  
   473  	// Check if VApp Children is populated
   474  	if vapp.VApp.Children == nil {
   475  		return Task{}, fmt.Errorf("vApp doesn't contain any children, interrupting customization")
   476  	}
   477  
   478  	vu := &types.GuestCustomizationSection{
   479  		Ovf:   types.XMLNamespaceOVF,
   480  		Xsi:   types.XMLNamespaceXSI,
   481  		Xmlns: types.XMLNamespaceVCloud,
   482  
   483  		HREF:                vapp.VApp.Children.VM[0].HREF,
   484  		Type:                types.MimeGuestCustomizationSection,
   485  		Info:                "Specifies Guest OS Customization Settings",
   486  		Enabled:             addrOf(true),
   487  		ComputerName:        computername,
   488  		CustomizationScript: script,
   489  		ChangeSid:           &changeSid,
   490  	}
   491  
   492  	apiEndpoint := urlParseRequestURI(vapp.VApp.Children.VM[0].HREF)
   493  	apiEndpoint.Path += "/guestCustomizationSection/"
   494  
   495  	// Return the task
   496  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut,
   497  		types.MimeGuestCustomizationSection, "error customizing VM: %s", vu)
   498  }
   499  
   500  func (vapp *VApp) GetStatus() (string, error) {
   501  	err := vapp.Refresh()
   502  	if err != nil {
   503  		return "", fmt.Errorf("error refreshing vApp: %s", err)
   504  	}
   505  	// Trying to make this function future-proof:
   506  	// If a new status is added to a future vCD API and the status map in types.go
   507  	// is not updated, we may get a panic.
   508  	// Using the ", ok" construct we take control of the data lookup and are able to fail
   509  	// gracefully.
   510  	statusText, ok := types.VAppStatuses[vapp.VApp.Status]
   511  	if ok {
   512  		return statusText, nil
   513  	}
   514  	return "", fmt.Errorf("status %d does not have a description in types.VappStatuses", vapp.VApp.Status)
   515  }
   516  
   517  // BlockWhileStatus blocks until the status of vApp exits unwantedStatus.
   518  // It sleeps 200 milliseconds between iterations and times out after timeOutAfterSeconds
   519  // of seconds.
   520  func (vapp *VApp) BlockWhileStatus(unwantedStatus string, timeOutAfterSeconds int) error {
   521  	timeoutAfter := time.After(time.Duration(timeOutAfterSeconds) * time.Second)
   522  	tick := time.NewTicker(200 * time.Millisecond)
   523  
   524  	for {
   525  		select {
   526  		case <-timeoutAfter:
   527  			return fmt.Errorf("timed out waiting for vApp to exit state %s after %d seconds",
   528  				unwantedStatus, timeOutAfterSeconds)
   529  		case <-tick.C:
   530  			currentStatus, err := vapp.GetStatus()
   531  
   532  			if err != nil {
   533  				return fmt.Errorf("could not get vApp status %s", err)
   534  			}
   535  			if currentStatus != unwantedStatus {
   536  				return nil
   537  			}
   538  		}
   539  	}
   540  }
   541  
   542  func (vapp *VApp) GetNetworkConnectionSection() (*types.NetworkConnectionSection, error) {
   543  
   544  	networkConnectionSection := &types.NetworkConnectionSection{}
   545  
   546  	if vapp.VApp.Children.VM[0].HREF == "" {
   547  		return networkConnectionSection, fmt.Errorf("cannot refresh, Object is empty")
   548  	}
   549  
   550  	_, err := vapp.client.ExecuteRequest(vapp.VApp.Children.VM[0].HREF+"/networkConnectionSection/", http.MethodGet,
   551  		types.MimeNetworkConnectionSection, "error retrieving network connection: %s", nil, networkConnectionSection)
   552  
   553  	// The request was successful
   554  	return networkConnectionSection, err
   555  }
   556  
   557  // Sets number of available virtual logical processors
   558  // (i.e. CPUs x cores per socket)
   559  // https://communities.vmware.com/thread/576209
   560  // Deprecated: Use vm.ChangeCPUcount()
   561  func (vapp *VApp) ChangeCPUCount(virtualCpuCount int) (Task, error) {
   562  	return vapp.ChangeCPUCountWithCore(virtualCpuCount, nil)
   563  }
   564  
   565  // Sets number of available virtual logical processors
   566  // (i.e. CPUs x cores per socket) and cores per socket.
   567  // Socket count is a result of: virtual logical processors/cores per socket
   568  // https://communities.vmware.com/thread/576209
   569  // Deprecated: Use vm.ChangeCPUCountWithCore()
   570  func (vapp *VApp) ChangeCPUCountWithCore(virtualCpuCount int, coresPerSocket *int) (Task, error) {
   571  
   572  	err := vapp.Refresh()
   573  	if err != nil {
   574  		return Task{}, fmt.Errorf("error refreshing vApp before running customization: %s", err)
   575  	}
   576  
   577  	// Check if VApp Children is populated
   578  	if vapp.VApp.Children == nil {
   579  		return Task{}, fmt.Errorf("vApp doesn't contain any children, interrupting customization")
   580  	}
   581  
   582  	newcpu := &types.OVFItem{
   583  		XmlnsRasd:       types.XMLNamespaceRASD,
   584  		XmlnsVCloud:     types.XMLNamespaceVCloud,
   585  		XmlnsXsi:        types.XMLNamespaceXSI,
   586  		XmlnsVmw:        types.XMLNamespaceVMW,
   587  		VCloudHREF:      vapp.VApp.Children.VM[0].HREF + "/virtualHardwareSection/cpu",
   588  		VCloudType:      types.MimeRasdItem,
   589  		AllocationUnits: "hertz * 10^6",
   590  		Description:     "Number of Virtual CPUs",
   591  		ElementName:     strconv.Itoa(virtualCpuCount) + " virtual CPU(s)",
   592  		InstanceID:      4,
   593  		Reservation:     0,
   594  		ResourceType:    types.ResourceTypeProcessor,
   595  		VirtualQuantity: int64(virtualCpuCount),
   596  		Weight:          0,
   597  		CoresPerSocket:  coresPerSocket,
   598  		Link: &types.Link{
   599  			HREF: vapp.VApp.Children.VM[0].HREF + "/virtualHardwareSection/cpu",
   600  			Rel:  "edit",
   601  			Type: types.MimeRasdItem,
   602  		},
   603  	}
   604  
   605  	apiEndpoint := urlParseRequestURI(vapp.VApp.Children.VM[0].HREF)
   606  	apiEndpoint.Path += "/virtualHardwareSection/cpu"
   607  
   608  	// Return the task
   609  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut,
   610  		types.MimeRasdItem, "error changing CPU count: %s", newcpu)
   611  }
   612  
   613  func (vapp *VApp) ChangeStorageProfile(name string) (Task, error) {
   614  	err := vapp.Refresh()
   615  	if err != nil {
   616  		return Task{}, fmt.Errorf("error refreshing vApp before running customization: %s", err)
   617  	}
   618  
   619  	if vapp.VApp.Children == nil || len(vapp.VApp.Children.VM) == 0 {
   620  		return Task{}, fmt.Errorf("vApp doesn't contain any children, interrupting customization")
   621  	}
   622  
   623  	vdc, err := vapp.GetParentVDC()
   624  	if err != nil {
   625  		return Task{}, fmt.Errorf("error retrieving parent VDC for vApp %s", vapp.VApp.Name)
   626  	}
   627  	storageProfileRef, err := vdc.FindStorageProfileReference(name)
   628  	if err != nil {
   629  		return Task{}, fmt.Errorf("error retrieving storage profile %s for vApp %s", name, vapp.VApp.Name)
   630  	}
   631  
   632  	newProfile := &types.Vm{
   633  		Name:           vapp.VApp.Children.VM[0].Name,
   634  		StorageProfile: &storageProfileRef,
   635  		Xmlns:          types.XMLNamespaceVCloud,
   636  	}
   637  
   638  	// Return the task
   639  	return vapp.client.ExecuteTaskRequest(vapp.VApp.Children.VM[0].HREF, http.MethodPut,
   640  		types.MimeVM, "error changing CPU count: %s", newProfile)
   641  }
   642  
   643  // Deprecated as it changes only first VM's name
   644  func (vapp *VApp) ChangeVMName(name string) (Task, error) {
   645  	err := vapp.Refresh()
   646  	if err != nil {
   647  		return Task{}, fmt.Errorf("error refreshing vApp before running customization: %s", err)
   648  	}
   649  
   650  	if vapp.VApp.Children == nil {
   651  		return Task{}, fmt.Errorf("vApp doesn't contain any children, interrupting customization")
   652  	}
   653  
   654  	newName := &types.Vm{
   655  		Name:  name,
   656  		Xmlns: types.XMLNamespaceVCloud,
   657  	}
   658  
   659  	// Return the task
   660  	return vapp.client.ExecuteTaskRequest(vapp.VApp.Children.VM[0].HREF, http.MethodPut,
   661  		types.MimeVM, "error changing VM name: %s", newName)
   662  }
   663  
   664  // SetOvf sets guest properties for the first child VM in vApp
   665  //
   666  // Deprecated: Use vm.SetProductSectionList()
   667  func (vapp *VApp) SetOvf(parameters map[string]string) (Task, error) {
   668  	err := vapp.Refresh()
   669  	if err != nil {
   670  		return Task{}, fmt.Errorf("error refreshing vApp before running customization: %s", err)
   671  	}
   672  
   673  	if vapp.VApp.Children == nil {
   674  		return Task{}, fmt.Errorf("vApp doesn't contain any children, interrupting customization")
   675  	}
   676  
   677  	if vapp.VApp.Children.VM[0].ProductSection == nil {
   678  		return Task{}, fmt.Errorf("vApp doesn't contain any children with ProductSection, interrupting customization")
   679  	}
   680  
   681  	for key, value := range parameters {
   682  		for _, ovf_value := range vapp.VApp.Children.VM[0].ProductSection.Property {
   683  			if ovf_value.Key == key {
   684  				ovf_value.Value = &types.Value{Value: value}
   685  				break
   686  			}
   687  		}
   688  	}
   689  
   690  	ovf := &types.ProductSectionList{
   691  		Xmlns:          types.XMLNamespaceVCloud,
   692  		Ovf:            types.XMLNamespaceOVF,
   693  		ProductSection: vapp.VApp.Children.VM[0].ProductSection,
   694  	}
   695  
   696  	apiEndpoint := urlParseRequestURI(vapp.VApp.Children.VM[0].HREF)
   697  	apiEndpoint.Path += "/productSections"
   698  
   699  	// Return the task
   700  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut,
   701  		types.MimeProductSection, "error setting ovf: %s", ovf)
   702  }
   703  
   704  func (vapp *VApp) ChangeNetworkConfig(networks []map[string]interface{}, ip string) (Task, error) {
   705  	err := vapp.Refresh()
   706  	if err != nil {
   707  		return Task{}, fmt.Errorf("error refreshing VM before running customization: %s", err)
   708  	}
   709  
   710  	if vapp.VApp.Children == nil {
   711  		return Task{}, fmt.Errorf("vApp doesn't contain any children, interrupting customization")
   712  	}
   713  
   714  	networksection, err := vapp.GetNetworkConnectionSection()
   715  	if err != nil {
   716  		return Task{}, err
   717  	}
   718  
   719  	for index, network := range networks {
   720  		// Determine what type of address is requested for the vApp
   721  		ipAllocationMode := types.IPAllocationModeNone
   722  		ipAddress := "Any"
   723  
   724  		// TODO: Review current behaviour of using DHCP when left blank
   725  		if ip == "" || ip == "dhcp" || network["ip"] == "dhcp" {
   726  			ipAllocationMode = types.IPAllocationModeDHCP
   727  		} else if ip == "allocated" || network["ip"] == "allocated" {
   728  			ipAllocationMode = types.IPAllocationModePool
   729  		} else if ip == "none" || network["ip"] == "none" {
   730  			ipAllocationMode = types.IPAllocationModeNone
   731  		} else if ip != "" || network["ip"] != "" {
   732  			ipAllocationMode = types.IPAllocationModeManual
   733  			// TODO: Check a valid IP has been given
   734  			ipAddress = ip
   735  		}
   736  
   737  		util.Logger.Printf("[DEBUG] Function ChangeNetworkConfig() for %s invoked", network["orgnetwork"])
   738  
   739  		networksection.Xmlns = types.XMLNamespaceVCloud
   740  		networksection.Ovf = types.XMLNamespaceOVF
   741  		networksection.Info = "Specifies the available VM network connections"
   742  
   743  		networksection.NetworkConnection[index].NeedsCustomization = true
   744  		networksection.NetworkConnection[index].IPAddress = ipAddress
   745  		networksection.NetworkConnection[index].IPAddressAllocationMode = ipAllocationMode
   746  		networksection.NetworkConnection[index].MACAddress = ""
   747  
   748  		if network["is_primary"] == true {
   749  			networksection.PrimaryNetworkConnectionIndex = index
   750  		}
   751  
   752  	}
   753  
   754  	apiEndpoint := urlParseRequestURI(vapp.VApp.Children.VM[0].HREF)
   755  	apiEndpoint.Path += "/networkConnectionSection/"
   756  
   757  	// Return the task
   758  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut,
   759  		types.MimeNetworkConnectionSection, "error changing network config: %s", networksection)
   760  }
   761  
   762  // Deprecated as it changes only first VM's memory
   763  func (vapp *VApp) ChangeMemorySize(size int) (Task, error) {
   764  
   765  	err := vapp.Refresh()
   766  	if err != nil {
   767  		return Task{}, fmt.Errorf("error refreshing vApp before running customization: %s", err)
   768  	}
   769  
   770  	// Check if VApp Children is populated
   771  	if vapp.VApp.Children == nil {
   772  		return Task{}, fmt.Errorf("vApp doesn't contain any children, interrupting customization")
   773  	}
   774  
   775  	newMem := &types.OVFItem{
   776  		XmlnsRasd:       types.XMLNamespaceRASD,
   777  		XmlnsVCloud:     types.XMLNamespaceVCloud,
   778  		XmlnsXsi:        types.XMLNamespaceXSI,
   779  		VCloudHREF:      vapp.VApp.Children.VM[0].HREF + "/virtualHardwareSection/memory",
   780  		VCloudType:      types.MimeRasdItem,
   781  		AllocationUnits: "byte * 2^20",
   782  		Description:     "Memory Size",
   783  		ElementName:     strconv.Itoa(size) + " MB of memory",
   784  		InstanceID:      5,
   785  		Reservation:     0,
   786  		ResourceType:    types.ResourceTypeMemory,
   787  		VirtualQuantity: int64(size),
   788  		Weight:          0,
   789  		Link: &types.Link{
   790  			HREF: vapp.VApp.Children.VM[0].HREF + "/virtualHardwareSection/memory",
   791  			Rel:  "edit",
   792  			Type: types.MimeRasdItem,
   793  		},
   794  	}
   795  
   796  	apiEndpoint := urlParseRequestURI(vapp.VApp.Children.VM[0].HREF)
   797  	apiEndpoint.Path += "/virtualHardwareSection/memory"
   798  
   799  	// Return the task
   800  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut,
   801  		types.MimeRasdItem, "error changing memory size: %s", newMem)
   802  }
   803  
   804  func (vapp *VApp) GetNetworkConfig() (*types.NetworkConfigSection, error) {
   805  
   806  	networkConfig := &types.NetworkConfigSection{}
   807  
   808  	if vapp.VApp.HREF == "" {
   809  		return networkConfig, fmt.Errorf("cannot refresh, Object is empty")
   810  	}
   811  
   812  	_, err := vapp.client.ExecuteRequest(vapp.VApp.HREF+"/networkConfigSection/", http.MethodGet,
   813  		types.MimeNetworkConfigSection, "error retrieving network config: %s", nil, networkConfig)
   814  
   815  	// The request was successful
   816  	return networkConfig, err
   817  }
   818  
   819  // AddRAWNetworkConfig adds existing VDC network to vApp
   820  // Deprecated: in favor of vapp.AddOrgNetwork
   821  func (vapp *VApp) AddRAWNetworkConfig(orgvdcnetworks []*types.OrgVDCNetwork) (Task, error) {
   822  
   823  	vAppNetworkConfig, err := vapp.GetNetworkConfig()
   824  	if err != nil {
   825  		return Task{}, fmt.Errorf("error getting vApp networks: %s", err)
   826  	}
   827  	networkConfigurations := vAppNetworkConfig.NetworkConfig
   828  
   829  	for _, network := range orgvdcnetworks {
   830  		networkConfigurations = append(networkConfigurations,
   831  			types.VAppNetworkConfiguration{
   832  				NetworkName: network.Name,
   833  				Configuration: &types.NetworkConfiguration{
   834  					ParentNetwork: &types.Reference{
   835  						HREF: network.HREF,
   836  					},
   837  					FenceMode: types.FenceModeBridged,
   838  				},
   839  			},
   840  		)
   841  	}
   842  
   843  	return updateNetworkConfigurations(vapp, networkConfigurations)
   844  }
   845  
   846  // Function allows to create isolated network for vApp. This is equivalent to vCD UI function - vApp network creation.
   847  // Deprecated: in favor of vapp.CreateVappNetwork
   848  func (vapp *VApp) AddIsolatedNetwork(newIsolatedNetworkSettings *VappNetworkSettings) (Task, error) {
   849  
   850  	err := validateNetworkConfigSettings(newIsolatedNetworkSettings)
   851  	if err != nil {
   852  		return Task{}, err
   853  	}
   854  
   855  	// for case when range is one ip address
   856  	if newIsolatedNetworkSettings.DhcpSettings != nil && newIsolatedNetworkSettings.DhcpSettings.IPRange != nil && newIsolatedNetworkSettings.DhcpSettings.IPRange.EndAddress == "" {
   857  		newIsolatedNetworkSettings.DhcpSettings.IPRange.EndAddress = newIsolatedNetworkSettings.DhcpSettings.IPRange.StartAddress
   858  	}
   859  
   860  	// only add values if available. Won't be send to API if not provided
   861  	var networkFeatures *types.NetworkFeatures
   862  	if newIsolatedNetworkSettings.DhcpSettings != nil {
   863  		networkFeatures = &types.NetworkFeatures{DhcpService: &types.DhcpService{
   864  			IsEnabled:        newIsolatedNetworkSettings.DhcpSettings.IsEnabled,
   865  			DefaultLeaseTime: newIsolatedNetworkSettings.DhcpSettings.DefaultLeaseTime,
   866  			MaxLeaseTime:     newIsolatedNetworkSettings.DhcpSettings.MaxLeaseTime,
   867  			IPRange:          newIsolatedNetworkSettings.DhcpSettings.IPRange}}
   868  	}
   869  
   870  	networkConfigurations := vapp.VApp.NetworkConfigSection.NetworkConfig
   871  	networkConfigurations = append(networkConfigurations,
   872  		types.VAppNetworkConfiguration{
   873  			NetworkName: newIsolatedNetworkSettings.Name,
   874  			Description: newIsolatedNetworkSettings.Description,
   875  			Configuration: &types.NetworkConfiguration{
   876  				FenceMode:        types.FenceModeIsolated,
   877  				GuestVlanAllowed: newIsolatedNetworkSettings.GuestVLANAllowed,
   878  				Features:         networkFeatures,
   879  				IPScopes: &types.IPScopes{IPScope: []*types.IPScope{&types.IPScope{IsInherited: false, Gateway: newIsolatedNetworkSettings.Gateway,
   880  					Netmask: newIsolatedNetworkSettings.NetMask, DNS1: newIsolatedNetworkSettings.DNS1,
   881  					DNS2: newIsolatedNetworkSettings.DNS2, DNSSuffix: newIsolatedNetworkSettings.DNSSuffix, IsEnabled: true,
   882  					IPRanges: &types.IPRanges{IPRange: newIsolatedNetworkSettings.StaticIPRanges}}}},
   883  			},
   884  			IsDeployed: false,
   885  		})
   886  
   887  	return updateNetworkConfigurations(vapp, networkConfigurations)
   888  
   889  }
   890  
   891  // CreateVappNetwork creates isolated or nat routed(connected to Org VDC network) network for vApp.
   892  // Returns pointer to types.NetworkConfigSection or error
   893  // If orgNetwork is nil, then isolated network created.
   894  func (vapp *VApp) CreateVappNetwork(newNetworkSettings *VappNetworkSettings, orgNetwork *types.OrgVDCNetwork) (*types.NetworkConfigSection, error) {
   895  	task, err := vapp.CreateVappNetworkAsync(newNetworkSettings, orgNetwork)
   896  	if err != nil {
   897  		return nil, err
   898  	}
   899  	err = task.WaitTaskCompletion()
   900  	if err != nil {
   901  		return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err))
   902  	}
   903  
   904  	vAppNetworkConfig, err := vapp.GetNetworkConfig()
   905  	if err != nil {
   906  		return nil, fmt.Errorf("error getting vApp networks: %#v", err)
   907  	}
   908  
   909  	return vAppNetworkConfig, nil
   910  }
   911  
   912  // CreateVappNetworkAsync creates asynchronously isolated or nat routed network for vApp. Returns Task or error
   913  // If orgNetwork is nil, then isolated network created.
   914  func (vapp *VApp) CreateVappNetworkAsync(newNetworkSettings *VappNetworkSettings, orgNetwork *types.OrgVDCNetwork) (Task, error) {
   915  
   916  	err := validateNetworkConfigSettings(newNetworkSettings)
   917  	if err != nil {
   918  		return Task{}, err
   919  	}
   920  
   921  	// for case when range is one ip address
   922  	if newNetworkSettings.DhcpSettings != nil && newNetworkSettings.DhcpSettings.IPRange != nil && newNetworkSettings.DhcpSettings.IPRange.EndAddress == "" {
   923  		newNetworkSettings.DhcpSettings.IPRange.EndAddress = newNetworkSettings.DhcpSettings.IPRange.StartAddress
   924  	}
   925  
   926  	// only add values if available. Won't be send to API if not provided
   927  	var networkFeatures *types.NetworkFeatures
   928  	if newNetworkSettings.DhcpSettings != nil {
   929  		networkFeatures = &types.NetworkFeatures{DhcpService: &types.DhcpService{
   930  			IsEnabled:        newNetworkSettings.DhcpSettings.IsEnabled,
   931  			DefaultLeaseTime: newNetworkSettings.DhcpSettings.DefaultLeaseTime,
   932  			MaxLeaseTime:     newNetworkSettings.DhcpSettings.MaxLeaseTime,
   933  			IPRange:          newNetworkSettings.DhcpSettings.IPRange},
   934  		}
   935  	}
   936  
   937  	networkConfigurations := vapp.VApp.NetworkConfigSection.NetworkConfig
   938  	vappConfiguration := types.VAppNetworkConfiguration{
   939  		NetworkName: newNetworkSettings.Name,
   940  		Description: newNetworkSettings.Description,
   941  		Configuration: &types.NetworkConfiguration{
   942  			FenceMode:        types.FenceModeIsolated,
   943  			GuestVlanAllowed: newNetworkSettings.GuestVLANAllowed,
   944  			Features:         networkFeatures,
   945  			IPScopes: &types.IPScopes{
   946  				IPScope: []*types.IPScope{{
   947  					IsInherited:        false,
   948  					Gateway:            newNetworkSettings.Gateway,
   949  					Netmask:            newNetworkSettings.NetMask,
   950  					SubnetPrefixLength: newNetworkSettings.SubnetPrefixLength,
   951  					DNS1:               newNetworkSettings.DNS1,
   952  					DNS2:               newNetworkSettings.DNS2,
   953  					DNSSuffix:          newNetworkSettings.DNSSuffix,
   954  					IsEnabled:          true,
   955  					IPRanges:           &types.IPRanges{IPRange: newNetworkSettings.StaticIPRanges}}}},
   956  			RetainNetInfoAcrossDeployments: newNetworkSettings.RetainIpMacEnabled,
   957  		},
   958  		IsDeployed: false,
   959  	}
   960  
   961  	if orgNetwork != nil {
   962  		vappConfiguration.Configuration.ParentNetwork = &types.Reference{
   963  			HREF: orgNetwork.HREF,
   964  		}
   965  		vappConfiguration.Configuration.FenceMode = types.FenceModeNAT
   966  	}
   967  
   968  	networkConfigurations = append(networkConfigurations,
   969  		vappConfiguration)
   970  
   971  	return updateNetworkConfigurations(vapp, networkConfigurations)
   972  }
   973  
   974  // AddOrgNetwork adds Org VDC network as vApp network.
   975  // Returns pointer to types.NetworkConfigSection or error
   976  func (vapp *VApp) AddOrgNetwork(newNetworkSettings *VappNetworkSettings, orgNetwork *types.OrgVDCNetwork, isFenced bool) (*types.NetworkConfigSection, error) {
   977  	task, err := vapp.AddOrgNetworkAsync(newNetworkSettings, orgNetwork, isFenced)
   978  	if err != nil {
   979  		return nil, err
   980  	}
   981  	err = task.WaitTaskCompletion()
   982  	if err != nil {
   983  		return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err))
   984  	}
   985  
   986  	vAppNetworkConfig, err := vapp.GetNetworkConfig()
   987  	if err != nil {
   988  		return nil, fmt.Errorf("error getting vApp networks: %#v", err)
   989  	}
   990  
   991  	return vAppNetworkConfig, nil
   992  }
   993  
   994  // AddOrgNetworkAsync adds asynchronously Org VDC network as vApp network. Returns Task or error
   995  func (vapp *VApp) AddOrgNetworkAsync(newNetworkSettings *VappNetworkSettings, orgNetwork *types.OrgVDCNetwork, isFenced bool) (Task, error) {
   996  
   997  	if orgNetwork == nil {
   998  		return Task{}, errors.New("org VDC network is missing")
   999  	}
  1000  
  1001  	fenceMode := types.FenceModeBridged
  1002  	if isFenced {
  1003  		fenceMode = types.FenceModeNAT
  1004  	}
  1005  
  1006  	networkConfigurations := vapp.VApp.NetworkConfigSection.NetworkConfig
  1007  	vappConfiguration := types.VAppNetworkConfiguration{
  1008  		NetworkName: orgNetwork.Name,
  1009  		Configuration: &types.NetworkConfiguration{
  1010  			FenceMode: fenceMode,
  1011  			ParentNetwork: &types.Reference{
  1012  				HREF: orgNetwork.HREF,
  1013  			},
  1014  			RetainNetInfoAcrossDeployments: newNetworkSettings.RetainIpMacEnabled,
  1015  		},
  1016  		IsDeployed: false,
  1017  	}
  1018  	networkConfigurations = append(networkConfigurations,
  1019  		vappConfiguration)
  1020  
  1021  	return updateNetworkConfigurations(vapp, networkConfigurations)
  1022  
  1023  }
  1024  
  1025  // UpdateNetwork updates vApp networks (isolated or connected to Org VDC network)
  1026  // Returns pointer to types.NetworkConfigSection or error
  1027  func (vapp *VApp) UpdateNetwork(newNetworkSettings *VappNetworkSettings, orgNetwork *types.OrgVDCNetwork) (*types.NetworkConfigSection, error) {
  1028  	task, err := vapp.UpdateNetworkAsync(newNetworkSettings, orgNetwork)
  1029  	if err != nil {
  1030  		return nil, err
  1031  	}
  1032  	err = task.WaitTaskCompletion()
  1033  	if err != nil {
  1034  		return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err))
  1035  	}
  1036  
  1037  	vAppNetworkConfig, err := vapp.GetNetworkConfig()
  1038  	if err != nil {
  1039  		return nil, fmt.Errorf("error getting vApp networks: %#v", err)
  1040  	}
  1041  
  1042  	return vAppNetworkConfig, nil
  1043  }
  1044  
  1045  // UpdateNetworkAsync asynchronously updates vApp networks (isolated or connected to Org VDC network).
  1046  // Returns task or error
  1047  func (vapp *VApp) UpdateNetworkAsync(networkSettingsToUpdate *VappNetworkSettings, orgNetwork *types.OrgVDCNetwork) (Task, error) {
  1048  	util.Logger.Printf("[TRACE] UpdateNetworkAsync with values: %#v and connect to org network: %#v", networkSettingsToUpdate, orgNetwork)
  1049  	currentNetworkConfiguration, err := vapp.GetNetworkConfig()
  1050  	if err != nil {
  1051  		return Task{}, err
  1052  	}
  1053  	var networkToUpdate types.VAppNetworkConfiguration
  1054  	var networkToUpdateIndex int
  1055  	for index, networkConfig := range currentNetworkConfiguration.NetworkConfig {
  1056  		if networkConfig.Link != nil {
  1057  			uuid, err := GetUuidFromHref(networkConfig.Link.HREF, false)
  1058  			if err != nil {
  1059  				return Task{}, err
  1060  			}
  1061  			if uuid == extractUuid(networkSettingsToUpdate.ID) {
  1062  				networkToUpdate = networkConfig
  1063  				networkToUpdateIndex = index
  1064  				break
  1065  			}
  1066  		}
  1067  	}
  1068  
  1069  	if networkToUpdate == (types.VAppNetworkConfiguration{}) {
  1070  		return Task{}, fmt.Errorf("not found network to update with Id %s", networkSettingsToUpdate.ID)
  1071  	}
  1072  	if networkToUpdate.Configuration == nil {
  1073  		networkToUpdate.Configuration = &types.NetworkConfiguration{}
  1074  	}
  1075  	networkToUpdate.Configuration.RetainNetInfoAcrossDeployments = networkSettingsToUpdate.RetainIpMacEnabled
  1076  	// new network to connect
  1077  	if networkToUpdate.Configuration.ParentNetwork == nil && orgNetwork != nil {
  1078  		networkToUpdate.Configuration.FenceMode = types.FenceModeNAT
  1079  		networkToUpdate.Configuration.ParentNetwork = &types.Reference{HREF: orgNetwork.HREF}
  1080  	}
  1081  	// change network to connect
  1082  	if networkToUpdate.Configuration.ParentNetwork != nil && orgNetwork != nil && networkToUpdate.Configuration.ParentNetwork.HREF != orgNetwork.HREF {
  1083  		networkToUpdate.Configuration.ParentNetwork = &types.Reference{HREF: orgNetwork.HREF}
  1084  	}
  1085  	// remove network to connect
  1086  	if orgNetwork == nil {
  1087  		networkToUpdate.Configuration.FenceMode = types.FenceModeIsolated
  1088  		networkToUpdate.Configuration.ParentNetwork = nil
  1089  	}
  1090  	networkToUpdate.Description = networkSettingsToUpdate.Description
  1091  	networkToUpdate.NetworkName = networkSettingsToUpdate.Name
  1092  	networkToUpdate.Configuration.GuestVlanAllowed = networkSettingsToUpdate.GuestVLANAllowed
  1093  	networkToUpdate.Configuration.IPScopes.IPScope[0].Gateway = networkSettingsToUpdate.Gateway
  1094  	networkToUpdate.Configuration.IPScopes.IPScope[0].Netmask = networkSettingsToUpdate.NetMask
  1095  	networkToUpdate.Configuration.IPScopes.IPScope[0].DNS1 = networkSettingsToUpdate.DNS1
  1096  	networkToUpdate.Configuration.IPScopes.IPScope[0].DNS2 = networkSettingsToUpdate.DNS2
  1097  	networkToUpdate.Configuration.IPScopes.IPScope[0].DNSSuffix = networkSettingsToUpdate.DNSSuffix
  1098  	networkToUpdate.Configuration.IPScopes.IPScope[0].IPRanges = &types.IPRanges{IPRange: networkSettingsToUpdate.StaticIPRanges}
  1099  
  1100  	// for case when range is one ip address
  1101  	if networkSettingsToUpdate.DhcpSettings != nil && networkSettingsToUpdate.DhcpSettings.IPRange != nil && networkSettingsToUpdate.DhcpSettings.IPRange.EndAddress == "" {
  1102  		networkSettingsToUpdate.DhcpSettings.IPRange.EndAddress = networkSettingsToUpdate.DhcpSettings.IPRange.StartAddress
  1103  	}
  1104  
  1105  	if networkToUpdate.Configuration.Features == nil {
  1106  		networkToUpdate.Configuration.Features = &types.NetworkFeatures{}
  1107  	}
  1108  
  1109  	// remove DHCP config
  1110  	if networkSettingsToUpdate.DhcpSettings == nil {
  1111  		networkToUpdate.Configuration.Features.DhcpService = nil
  1112  	}
  1113  
  1114  	// create DHCP config
  1115  	if networkSettingsToUpdate.DhcpSettings != nil && networkToUpdate.Configuration.Features.DhcpService == nil {
  1116  		networkToUpdate.Configuration.Features.DhcpService = &types.DhcpService{
  1117  			IsEnabled:        networkSettingsToUpdate.DhcpSettings.IsEnabled,
  1118  			DefaultLeaseTime: networkSettingsToUpdate.DhcpSettings.DefaultLeaseTime,
  1119  			MaxLeaseTime:     networkSettingsToUpdate.DhcpSettings.MaxLeaseTime,
  1120  			IPRange:          networkSettingsToUpdate.DhcpSettings.IPRange}
  1121  	}
  1122  
  1123  	// update DHCP config
  1124  	if networkSettingsToUpdate.DhcpSettings != nil && networkToUpdate.Configuration.Features.DhcpService != nil {
  1125  		networkToUpdate.Configuration.Features.DhcpService.IsEnabled = networkSettingsToUpdate.DhcpSettings.IsEnabled
  1126  		networkToUpdate.Configuration.Features.DhcpService.DefaultLeaseTime = networkSettingsToUpdate.DhcpSettings.DefaultLeaseTime
  1127  		networkToUpdate.Configuration.Features.DhcpService.MaxLeaseTime = networkSettingsToUpdate.DhcpSettings.MaxLeaseTime
  1128  		networkToUpdate.Configuration.Features.DhcpService.IPRange = networkSettingsToUpdate.DhcpSettings.IPRange
  1129  	}
  1130  
  1131  	currentNetworkConfiguration.NetworkConfig[networkToUpdateIndex] = networkToUpdate
  1132  
  1133  	return updateNetworkConfigurations(vapp, currentNetworkConfiguration.NetworkConfig)
  1134  }
  1135  
  1136  // UpdateOrgNetwork updates Org VDC network which is part of a vApp
  1137  // Returns pointer to types.NetworkConfigSection or error
  1138  func (vapp *VApp) UpdateOrgNetwork(newNetworkSettings *VappNetworkSettings, isFenced bool) (*types.NetworkConfigSection, error) {
  1139  	task, err := vapp.UpdateOrgNetworkAsync(newNetworkSettings, isFenced)
  1140  	if err != nil {
  1141  		return nil, err
  1142  	}
  1143  	err = task.WaitTaskCompletion()
  1144  	if err != nil {
  1145  		return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err))
  1146  	}
  1147  
  1148  	vAppNetworkConfig, err := vapp.GetNetworkConfig()
  1149  	if err != nil {
  1150  		return nil, fmt.Errorf("error getting vApp networks: %#v", err)
  1151  	}
  1152  
  1153  	return vAppNetworkConfig, nil
  1154  }
  1155  
  1156  // UpdateOrgNetworkAsync asynchronously updates Org VDC network which is part of a vApp
  1157  // Returns task or error
  1158  func (vapp *VApp) UpdateOrgNetworkAsync(networkSettingsToUpdate *VappNetworkSettings, isFenced bool) (Task, error) {
  1159  	util.Logger.Printf("[TRACE] UpdateOrgNetworkAsync with values: %#v ", networkSettingsToUpdate)
  1160  	currentNetworkConfiguration, err := vapp.GetNetworkConfig()
  1161  	if err != nil {
  1162  		return Task{}, err
  1163  	}
  1164  	var networkToUpdate types.VAppNetworkConfiguration
  1165  	var networkToUpdateIndex int
  1166  
  1167  	for index, networkConfig := range currentNetworkConfiguration.NetworkConfig {
  1168  		if networkConfig.Link != nil {
  1169  			uuid, err := GetUuidFromHref(networkConfig.Link.HREF, false)
  1170  			if err != nil {
  1171  				return Task{}, err
  1172  			}
  1173  
  1174  			if uuid == extractUuid(networkSettingsToUpdate.ID) {
  1175  				networkToUpdate = networkConfig
  1176  				networkToUpdateIndex = index
  1177  				break
  1178  			}
  1179  		}
  1180  	}
  1181  
  1182  	if networkToUpdate == (types.VAppNetworkConfiguration{}) {
  1183  		return Task{}, fmt.Errorf("not found network to update with Id %s", networkSettingsToUpdate.ID)
  1184  	}
  1185  
  1186  	fenceMode := types.FenceModeBridged
  1187  	if isFenced {
  1188  		fenceMode = types.FenceModeNAT
  1189  	}
  1190  
  1191  	if networkToUpdate.Configuration == nil {
  1192  		networkToUpdate.Configuration = &types.NetworkConfiguration{}
  1193  	}
  1194  	networkToUpdate.Configuration.RetainNetInfoAcrossDeployments = networkSettingsToUpdate.RetainIpMacEnabled
  1195  	networkToUpdate.Configuration.FenceMode = fenceMode
  1196  
  1197  	currentNetworkConfiguration.NetworkConfig[networkToUpdateIndex] = networkToUpdate
  1198  
  1199  	return updateNetworkConfigurations(vapp, currentNetworkConfiguration.NetworkConfig)
  1200  }
  1201  
  1202  func validateNetworkConfigSettings(networkSettings *VappNetworkSettings) error {
  1203  	if networkSettings.Name == "" {
  1204  		return errors.New("network name is missing")
  1205  	}
  1206  
  1207  	if networkSettings.Gateway == "" {
  1208  		return errors.New("network gateway IP is missing")
  1209  	}
  1210  
  1211  	if networkSettings.NetMask == "" && networkSettings.SubnetPrefixLength == "" {
  1212  		return errors.New("network mask and subnet prefix length config is missing, exactly one is required")
  1213  	}
  1214  
  1215  	if networkSettings.NetMask != "" && networkSettings.SubnetPrefixLength != "" {
  1216  		return errors.New("exactly one of netmask and prefix length can be supplied")
  1217  	}
  1218  
  1219  	if networkSettings.DhcpSettings != nil && networkSettings.DhcpSettings.IPRange == nil {
  1220  		return errors.New("network DHCP ip range config is missing")
  1221  	}
  1222  
  1223  	if networkSettings.DhcpSettings != nil && networkSettings.DhcpSettings.IPRange.StartAddress == "" {
  1224  		return errors.New("network DHCP ip range start address is missing")
  1225  	}
  1226  
  1227  	return nil
  1228  }
  1229  
  1230  // RemoveNetwork removes any network (be it isolated or connected to an Org Network) from vApp
  1231  // Returns pointer to types.NetworkConfigSection or error
  1232  func (vapp *VApp) RemoveNetwork(identifier string) (*types.NetworkConfigSection, error) {
  1233  	task, err := vapp.RemoveNetworkAsync(identifier)
  1234  	if err != nil {
  1235  		return nil, err
  1236  	}
  1237  	err = task.WaitTaskCompletion()
  1238  	if err != nil {
  1239  		return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err))
  1240  	}
  1241  
  1242  	vAppNetworkConfig, err := vapp.GetNetworkConfig()
  1243  	if err != nil {
  1244  		return nil, fmt.Errorf("error getting vApp networks: %#v", err)
  1245  	}
  1246  
  1247  	return vAppNetworkConfig, nil
  1248  }
  1249  
  1250  // RemoveNetworkAsync asynchronously removes any network (be it isolated or connected to an Org Network) from vApp
  1251  // Accepts network ID or name
  1252  func (vapp *VApp) RemoveNetworkAsync(identifier string) (Task, error) {
  1253  
  1254  	if identifier == "" {
  1255  		return Task{}, fmt.Errorf("network ID/name can't be empty")
  1256  	}
  1257  
  1258  	networkConfigurations := vapp.VApp.NetworkConfigSection.NetworkConfig
  1259  	for _, networkConfig := range networkConfigurations {
  1260  		networkId, err := GetUuidFromHref(networkConfig.Link.HREF, false)
  1261  		if err != nil {
  1262  			return Task{}, fmt.Errorf("unable to get network ID from HREF: %s", err)
  1263  		}
  1264  		if networkId == extractUuid(identifier) || networkConfig.NetworkName == identifier {
  1265  			deleteUrl := vapp.client.VCDHREF.String() + "/network/" + networkId
  1266  			errMessage := fmt.Sprintf("detaching vApp network %s (id '%s'): %%s", networkConfig.NetworkName, networkId)
  1267  			task, err := vapp.client.ExecuteTaskRequest(deleteUrl, http.MethodDelete, types.AnyXMLMime, errMessage, nil)
  1268  			if err != nil {
  1269  				return Task{}, err
  1270  			}
  1271  
  1272  			return task, nil
  1273  		}
  1274  	}
  1275  
  1276  	return Task{}, fmt.Errorf("network to remove %s, wasn't found", identifier)
  1277  
  1278  }
  1279  
  1280  // Removes vApp isolated network
  1281  // Deprecated: in favor vapp.RemoveNetwork
  1282  func (vapp *VApp) RemoveIsolatedNetwork(networkName string) (Task, error) {
  1283  
  1284  	if networkName == "" {
  1285  		return Task{}, fmt.Errorf("network name can't be empty")
  1286  	}
  1287  
  1288  	networkConfigurations := vapp.VApp.NetworkConfigSection.NetworkConfig
  1289  	isNetworkFound := false
  1290  	for index, networkConfig := range networkConfigurations {
  1291  		if networkConfig.NetworkName == networkName {
  1292  			isNetworkFound = true
  1293  			networkConfigurations = append(networkConfigurations[:index], networkConfigurations[index+1:]...)
  1294  		}
  1295  	}
  1296  
  1297  	if !isNetworkFound {
  1298  		return Task{}, fmt.Errorf("network to remove %s, wasn't found", networkName)
  1299  	}
  1300  
  1301  	return updateNetworkConfigurations(vapp, networkConfigurations)
  1302  }
  1303  
  1304  // Function allows to update vApp network configuration. This works for updating, deleting and adding.
  1305  // Network configuration has to be full with new, changed elements and unchanged.
  1306  // https://opengrok.eng.vmware.com/source/xref/cloud-sp-main.perforce-shark.1700/sp-main/dev-integration/system-tests/SystemTests/src/main/java/com/vmware/cloud/systemtests/util/VAppNetworkUtils.java#createVAppNetwork
  1307  // http://pubs.vmware.com/vcloud-api-1-5/wwhelp/wwhimpl/js/html/wwhelp.htm#href=api_prog/GUID-92622A15-E588-4FA1-92DA-A22A4757F2A0.html#1_14_12_10_1
  1308  func updateNetworkConfigurations(vapp *VApp, networkConfigurations []types.VAppNetworkConfiguration) (Task, error) {
  1309  	util.Logger.Printf("[TRACE] updateNetworkConfigurations for vAPP: %#v and network config: %#v", vapp, networkConfigurations)
  1310  	networkConfig := &types.NetworkConfigSection{
  1311  		Info:          "Configuration parameters for logical networks",
  1312  		Ovf:           types.XMLNamespaceOVF,
  1313  		Type:          types.MimeNetworkConfigSection,
  1314  		Xmlns:         types.XMLNamespaceVCloud,
  1315  		NetworkConfig: networkConfigurations,
  1316  	}
  1317  
  1318  	apiEndpoint := urlParseRequestURI(vapp.VApp.HREF)
  1319  	apiEndpoint.Path += "/networkConfigSection/"
  1320  
  1321  	// Return the task
  1322  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut,
  1323  		types.MimeNetworkConfigSection, "error updating vApp Network: %s", networkConfig)
  1324  }
  1325  
  1326  // RemoveAllNetworks detaches all networks from vApp
  1327  func (vapp *VApp) RemoveAllNetworks() (Task, error) {
  1328  	return updateNetworkConfigurations(vapp, []types.VAppNetworkConfiguration{})
  1329  }
  1330  
  1331  // SetProductSectionList sets product section for a vApp. It allows to change vApp guest properties.
  1332  //
  1333  // The slice of properties "ProductSectionList.ProductSection.Property" is not necessarily ordered
  1334  // or returned as set before
  1335  func (vapp *VApp) SetProductSectionList(productSection *types.ProductSectionList) (*types.ProductSectionList, error) {
  1336  	err := setProductSectionList(vapp.client, vapp.VApp.HREF, productSection)
  1337  	if err != nil {
  1338  		return nil, fmt.Errorf("unable to set vApp product section: %s", err)
  1339  	}
  1340  
  1341  	return vapp.GetProductSectionList()
  1342  }
  1343  
  1344  // GetProductSectionList retrieves product section for a vApp. It allows to read vApp guest properties.
  1345  //
  1346  // The slice of properties "ProductSectionList.ProductSection.Property" is not necessarily ordered
  1347  // or returned as set before
  1348  func (vapp *VApp) GetProductSectionList() (*types.ProductSectionList, error) {
  1349  	return getProductSectionList(vapp.client, vapp.VApp.HREF)
  1350  }
  1351  
  1352  // GetVMByName returns a VM reference if the VM name matches an existing one.
  1353  // If no valid VM is found, it returns a nil VM reference and an error
  1354  func (vapp *VApp) GetVMByName(vmName string, refresh bool) (*VM, error) {
  1355  	if refresh {
  1356  		err := vapp.Refresh()
  1357  		if err != nil {
  1358  			return nil, fmt.Errorf("error refreshing vApp: %s", err)
  1359  		}
  1360  	}
  1361  
  1362  	//vApp Might Not Have Any VMs
  1363  	if vapp.VApp.Children == nil {
  1364  		return nil, ErrorEntityNotFound
  1365  	}
  1366  
  1367  	util.Logger.Printf("[TRACE] Looking for VM: %s", vmName)
  1368  	for _, child := range vapp.VApp.Children.VM {
  1369  
  1370  		util.Logger.Printf("[TRACE] Looking at: %s", child.Name)
  1371  		if child.Name == vmName {
  1372  			return vapp.client.GetVMByHref(child.HREF)
  1373  		}
  1374  
  1375  	}
  1376  	util.Logger.Printf("[TRACE] Couldn't find VM: %s", vmName)
  1377  	return nil, ErrorEntityNotFound
  1378  }
  1379  
  1380  // GetVMById returns a VM reference if the VM ID matches an existing one.
  1381  // If no valid VM is found, it returns a nil VM reference and an error
  1382  func (vapp *VApp) GetVMById(id string, refresh bool) (*VM, error) {
  1383  	if refresh {
  1384  		err := vapp.Refresh()
  1385  		if err != nil {
  1386  			return nil, fmt.Errorf("error refreshing vApp: %s", err)
  1387  		}
  1388  	}
  1389  
  1390  	//vApp Might Not Have Any VMs
  1391  	if vapp.VApp.Children == nil {
  1392  		return nil, ErrorEntityNotFound
  1393  	}
  1394  
  1395  	util.Logger.Printf("[TRACE] Looking for VM: %s", id)
  1396  	for _, child := range vapp.VApp.Children.VM {
  1397  
  1398  		util.Logger.Printf("[TRACE] Looking at: %s", child.Name)
  1399  		if equalIds(id, child.ID, child.HREF) {
  1400  			return vapp.client.GetVMByHref(child.HREF)
  1401  		}
  1402  	}
  1403  	util.Logger.Printf("[TRACE] Couldn't find VM: %s", id)
  1404  	return nil, ErrorEntityNotFound
  1405  }
  1406  
  1407  // GetVMByNameOrId returns a VM reference if either the VM name or ID matches an existing one.
  1408  // If no valid VM is found, it returns a nil VM reference and an error
  1409  func (vapp *VApp) GetVMByNameOrId(identifier string, refresh bool) (*VM, error) {
  1410  	getByName := func(name string, refresh bool) (interface{}, error) { return vapp.GetVMByName(name, refresh) }
  1411  	getById := func(id string, refresh bool) (interface{}, error) { return vapp.GetVMById(id, refresh) }
  1412  	entity, err := getEntityByNameOrId(getByName, getById, identifier, false)
  1413  	if entity == nil {
  1414  		return nil, err
  1415  	}
  1416  	return entity.(*VM), err
  1417  }
  1418  
  1419  // QueryVappList returns a list of all vApps in all the organizations available to the caller
  1420  func (client *Client) QueryVappList() ([]*types.QueryResultVAppRecordType, error) {
  1421  	var vappList []*types.QueryResultVAppRecordType
  1422  	queryType := client.GetQueryType(types.QtVapp)
  1423  	params := map[string]string{
  1424  		"type":          queryType,
  1425  		"filterEncoded": "true",
  1426  	}
  1427  	vappResult, err := client.cumulativeQuery(queryType, nil, params)
  1428  	if err != nil {
  1429  		return nil, fmt.Errorf("error getting vApp list : %s", err)
  1430  	}
  1431  	vappList = vappResult.Results.VAppRecord
  1432  	if client.IsSysAdmin {
  1433  		vappList = vappResult.Results.AdminVAppRecord
  1434  	}
  1435  	return vappList, nil
  1436  }
  1437  
  1438  // getOrgInfo finds the organization to which the vApp belongs (through the VDC), and returns its name and ID
  1439  func (vapp *VApp) getOrgInfo() (*TenantContext, error) {
  1440  	previous, exists := orgInfoCache[vapp.VApp.ID]
  1441  	if exists {
  1442  		return previous, nil
  1443  	}
  1444  	var err error
  1445  	vdc, err := vapp.GetParentVDC()
  1446  	if err != nil {
  1447  		return nil, err
  1448  	}
  1449  	return vdc.getTenantContext()
  1450  }
  1451  
  1452  // UpdateNameDescription can change the name and the description of a vApp
  1453  // If name is empty, it is left unchanged.
  1454  func (vapp *VApp) UpdateNameDescription(newName, newDescription string) error {
  1455  	if vapp == nil || vapp.VApp.HREF == "" {
  1456  		return fmt.Errorf("vApp or href cannot be empty")
  1457  	}
  1458  
  1459  	// Skip update if we are using the original values
  1460  	if (newName == vapp.VApp.Name || newName == "") && (newDescription == vapp.VApp.Description) {
  1461  		return nil
  1462  	}
  1463  
  1464  	opType := types.MimeRecomposeVappParams
  1465  
  1466  	href := ""
  1467  	for _, link := range vapp.VApp.Link {
  1468  		if link.Type == opType && link.Rel == "recompose" {
  1469  			href = link.HREF
  1470  			break
  1471  		}
  1472  	}
  1473  
  1474  	if href == "" {
  1475  		return fmt.Errorf("no appropriate link for update found for vApp %s", vapp.VApp.Name)
  1476  	}
  1477  
  1478  	if newName == "" {
  1479  		newName = vapp.VApp.Name
  1480  	}
  1481  
  1482  	recomposeParams := &types.SmallRecomposeVappParams{
  1483  		XMLName:     xml.Name{},
  1484  		Ovf:         types.XMLNamespaceOVF,
  1485  		Xsi:         types.XMLNamespaceXSI,
  1486  		Xmlns:       types.XMLNamespaceVCloud,
  1487  		Name:        newName,
  1488  		Description: newDescription,
  1489  		Deploy:      vapp.VApp.Deployed,
  1490  	}
  1491  
  1492  	task, err := vapp.client.ExecuteTaskRequest(href, http.MethodPost,
  1493  		opType, "error updating vapp: %s", recomposeParams)
  1494  
  1495  	if err != nil {
  1496  		return fmt.Errorf("unable to update vApp: %s", err)
  1497  	}
  1498  
  1499  	err = task.WaitTaskCompletion()
  1500  	if err != nil {
  1501  		return fmt.Errorf("task for updating vApp failed: %s", err)
  1502  	}
  1503  	return vapp.Refresh()
  1504  }
  1505  
  1506  // UpdateDescription changes the description of a vApp
  1507  func (vapp *VApp) UpdateDescription(newDescription string) error {
  1508  	return vapp.UpdateNameDescription("", newDescription)
  1509  }
  1510  
  1511  // Rename changes the name of a vApp
  1512  func (vapp *VApp) Rename(newName string) error {
  1513  	return vapp.UpdateNameDescription(newName, vapp.VApp.Description)
  1514  }
  1515  
  1516  func (vapp *VApp) getTenantContext() (*TenantContext, error) {
  1517  	parentVdc, err := vapp.GetParentVDC()
  1518  	if err != nil {
  1519  		return nil, err
  1520  	}
  1521  	return parentVdc.getTenantContext()
  1522  }
  1523  
  1524  // RenewLease updates the lease terms for the vApp
  1525  func (vapp *VApp) RenewLease(deploymentLeaseInSeconds, storageLeaseInSeconds int) error {
  1526  
  1527  	href := ""
  1528  	if vapp.VApp.LeaseSettingsSection != nil {
  1529  		if vapp.VApp.LeaseSettingsSection.DeploymentLeaseInSeconds == deploymentLeaseInSeconds &&
  1530  			vapp.VApp.LeaseSettingsSection.StorageLeaseInSeconds == storageLeaseInSeconds {
  1531  			// Requested parameters are the same as existing parameters: exit without updating
  1532  			return nil
  1533  		}
  1534  		href = vapp.VApp.LeaseSettingsSection.HREF
  1535  	}
  1536  	if href == "" {
  1537  		for _, link := range vapp.VApp.Link {
  1538  			if link.Rel == "edit" && link.Type == types.MimeLeaseSettingSection {
  1539  				href = link.HREF
  1540  				break
  1541  			}
  1542  		}
  1543  	}
  1544  	if href == "" {
  1545  		return fmt.Errorf("link to update lease settings not found for vApp %s", vapp.VApp.Name)
  1546  	}
  1547  
  1548  	var leaseSettings = types.UpdateLeaseSettingsSection{
  1549  		HREF:                     href,
  1550  		XmlnsOvf:                 types.XMLNamespaceOVF,
  1551  		Xmlns:                    types.XMLNamespaceVCloud,
  1552  		OVFInfo:                  "Lease section settings",
  1553  		Type:                     types.MimeLeaseSettingSection,
  1554  		DeploymentLeaseInSeconds: &deploymentLeaseInSeconds,
  1555  		StorageLeaseInSeconds:    &storageLeaseInSeconds,
  1556  	}
  1557  
  1558  	task, err := vapp.client.ExecuteTaskRequest(href, http.MethodPut,
  1559  		types.MimeLeaseSettingSection, "error updating vapp lease : %s", &leaseSettings)
  1560  
  1561  	if err != nil {
  1562  		return fmt.Errorf("unable to update vApp lease: %s", err)
  1563  	}
  1564  
  1565  	err = task.WaitTaskCompletion()
  1566  	if err != nil {
  1567  		return fmt.Errorf("task for updating vApp lease failed: %s", err)
  1568  	}
  1569  	return vapp.Refresh()
  1570  }
  1571  
  1572  // GetLease retrieves the lease terms for a vApp
  1573  func (vapp *VApp) GetLease() (*types.LeaseSettingsSection, error) {
  1574  
  1575  	href := ""
  1576  	if vapp.VApp.LeaseSettingsSection != nil {
  1577  		href = vapp.VApp.LeaseSettingsSection.HREF
  1578  	}
  1579  	if href == "" {
  1580  		for _, link := range vapp.VApp.Link {
  1581  			if link.Type == types.MimeLeaseSettingSection {
  1582  				href = link.HREF
  1583  				break
  1584  			}
  1585  		}
  1586  	}
  1587  	if href == "" {
  1588  		return nil, fmt.Errorf("link to retrieve lease settings not found for vApp %s", vapp.VApp.Name)
  1589  	}
  1590  	var leaseSettings types.LeaseSettingsSection
  1591  
  1592  	_, err := vapp.client.ExecuteRequest(href, http.MethodGet, "", "error getting vApp lease info: %s", nil, &leaseSettings)
  1593  
  1594  	if err != nil {
  1595  		return nil, err
  1596  	}
  1597  	return &leaseSettings, nil
  1598  }