github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/vapp_network.go (about)

     1  /*
     2   * Copyright 2020 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
     3   */
     4  
     5  package govcd
     6  
     7  import (
     8  	"fmt"
     9  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    10  	"github.com/vmware/go-vcloud-director/v2/util"
    11  	"net/http"
    12  	"strings"
    13  )
    14  
    15  // UpdateNetworkFirewallRules updates vApp networks firewall rules. It will overwrite existing ones as there is
    16  // no 100% way to identify them separately.
    17  // Returns pointer to types.VAppNetwork or error
    18  func (vapp *VApp) UpdateNetworkFirewallRules(networkId string, firewallRules []*types.FirewallRule, enabled bool, defaultAction string, logDefaultAction bool) (*types.VAppNetwork, error) {
    19  	task, err := vapp.UpdateNetworkFirewallRulesAsync(networkId, firewallRules, enabled, defaultAction, logDefaultAction)
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  	err = task.WaitTaskCompletion()
    24  	if err != nil {
    25  		return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err))
    26  	}
    27  
    28  	return vapp.GetVappNetworkById(networkId, false)
    29  }
    30  
    31  // UpdateNetworkFirewallRulesAsync asynchronously updates vApp networks firewall rules. It will overwrite existing ones
    32  // as there is no 100% way to identify them separately.
    33  // Returns task or error
    34  func (vapp *VApp) UpdateNetworkFirewallRulesAsync(networkId string, firewallRules []*types.FirewallRule, enabled bool, defaultAction string, logDefaultAction bool) (Task, error) {
    35  	util.Logger.Printf("[TRACE] UpdateNetworkFirewallRulesAsync with values: id: %s and firewallServiceConfiguration: %#v", networkId, firewallRules)
    36  	uuid := extractUuid(networkId)
    37  	networkToUpdate, err := vapp.GetVappNetworkById(uuid, true)
    38  	if err != nil {
    39  		return Task{}, err
    40  	}
    41  
    42  	if networkToUpdate.Configuration.Features == nil {
    43  		networkToUpdate.Configuration.Features = &types.NetworkFeatures{}
    44  	}
    45  	networkToUpdate.Xmlns = types.XMLNamespaceVCloud
    46  
    47  	// If API didn't return Firewall service XML part, that means vApp network isn't connected to org network or not fenced.
    48  	// In other words there isn't firewall when you connected directly or isolated.
    49  	if networkToUpdate.Configuration.Features.FirewallService == nil {
    50  		return Task{}, fmt.Errorf("provided network isn't connecd to org network or isn't fenced")
    51  	}
    52  	networkToUpdate.Configuration.Features.FirewallService.IsEnabled = enabled
    53  	networkToUpdate.Configuration.Features.FirewallService.LogDefaultAction = logDefaultAction
    54  	networkToUpdate.Configuration.Features.FirewallService.DefaultAction = defaultAction
    55  	networkToUpdate.Configuration.Features.FirewallService.FirewallRule = firewallRules
    56  
    57  	// here we use `PUT /network/{id}` which allow to change vApp network.
    58  	// But `GET /network/{id}` can return org VDC network or vApp network.
    59  	apiEndpoint := vapp.client.VCDHREF
    60  	apiEndpoint.Path += "/network/" + uuid
    61  
    62  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut,
    63  		types.MimeVappNetwork, "error updating vApp Network firewall rules: %s", networkToUpdate)
    64  }
    65  
    66  // GetVappNetworkById returns a VApp network reference if the vApp network ID matches an existing one.
    67  // If no valid VApp network is found, it returns a nil VApp network reference and an error
    68  func (vapp *VApp) GetVappNetworkById(id string, refresh bool) (*types.VAppNetwork, error) {
    69  	util.Logger.Printf("[TRACE] [GetVappNetworkById] getting vApp Network: %s and refresh %t", id, refresh)
    70  
    71  	if refresh {
    72  		err := vapp.Refresh()
    73  		if err != nil {
    74  			return nil, fmt.Errorf("error refreshing vApp: %s", err)
    75  		}
    76  	}
    77  
    78  	//vApp Might Not Have Any networks
    79  	if vapp.VApp.NetworkConfigSection == nil || len(vapp.VApp.NetworkConfigSection.NetworkConfig) == 0 {
    80  		return nil, ErrorEntityNotFound
    81  	}
    82  
    83  	util.Logger.Printf("[TRACE] Looking for networks: %s --- %d", id, len(vapp.VApp.NetworkConfigSection.NetworkConfig))
    84  	for _, vappNetwork := range vapp.VApp.NetworkConfigSection.NetworkConfig {
    85  		// Break early for empty network interfaces. They don't have all information
    86  		if vappNetwork.NetworkName == types.NoneNetwork {
    87  			continue
    88  		}
    89  		util.Logger.Printf("[TRACE] Looking at: %s", vappNetwork.Link.HREF)
    90  		if equalIds(id, vappNetwork.ID, vappNetwork.Link.HREF) {
    91  			vappNetwork := &types.VAppNetwork{}
    92  
    93  			apiEndpoint := vapp.client.VCDHREF
    94  			apiEndpoint.Path += "/network/" + extractUuid(id)
    95  
    96  			_, err := vapp.client.ExecuteRequest(apiEndpoint.String(), http.MethodGet,
    97  				types.MimeVappNetwork, "error getting vApp network: %s", nil, vappNetwork)
    98  			if err != nil {
    99  				return nil, err
   100  			}
   101  			return vappNetwork, nil
   102  		}
   103  	}
   104  	util.Logger.Printf("[TRACE] GetVappNetworkById returns not found entity")
   105  	return nil, ErrorEntityNotFound
   106  }
   107  
   108  // GetVappNetworkByName returns a VAppNetwork reference if the vApp network name matches an existing one.
   109  // If no valid vApp network is found, it returns a nil VAppNetwork reference and an error
   110  func (vapp *VApp) GetVappNetworkByName(vappNetworkName string, refresh bool) (*types.VAppNetwork, error) {
   111  	util.Logger.Printf("[TRACE] [GetVappNetworkByName] getting vApp Network: %s and refresh %t", vappNetworkName, refresh)
   112  	if refresh {
   113  		err := vapp.Refresh()
   114  		if err != nil {
   115  			return nil, fmt.Errorf("error refreshing vApp: %s", err)
   116  		}
   117  	}
   118  
   119  	//vApp Might Not Have Any networks
   120  	if vapp.VApp.NetworkConfigSection == nil || len(vapp.VApp.NetworkConfigSection.NetworkConfig) == 0 {
   121  		return nil, ErrorEntityNotFound
   122  	}
   123  
   124  	util.Logger.Printf("[TRACE] Looking for networks: %s", vappNetworkName)
   125  	for _, vappNetwork := range vapp.VApp.NetworkConfigSection.NetworkConfig {
   126  
   127  		util.Logger.Printf("[TRACE] Looking at: %s", vappNetwork.NetworkName)
   128  		if vappNetwork.NetworkName == vappNetworkName {
   129  			return vapp.GetVappNetworkById(extractUuid(vappNetwork.Link.HREF), refresh)
   130  		}
   131  
   132  	}
   133  	util.Logger.Printf("[TRACE] Couldn't find vApp network: %s", vappNetworkName)
   134  	return nil, ErrorEntityNotFound
   135  }
   136  
   137  // GetVappNetworkByNameOrId returns a types.VAppNetwork reference if either the vApp network name or ID matches an existing one.
   138  // If no valid vApp network is found, it returns a nil types.VAppNetwork reference and an error
   139  func (vapp *VApp) GetVappNetworkByNameOrId(identifier string, refresh bool) (*types.VAppNetwork, error) {
   140  	getByName := func(name string, refresh bool) (interface{}, error) { return vapp.GetVappNetworkByName(name, refresh) }
   141  	getById := func(id string, refresh bool) (interface{}, error) { return vapp.GetVappNetworkById(id, refresh) }
   142  	entity, err := getEntityByNameOrId(getByName, getById, identifier, false)
   143  	if entity == nil {
   144  		return nil, err
   145  	}
   146  	return entity.(*types.VAppNetwork), err
   147  }
   148  
   149  // UpdateNetworkNatRules updates vApp networks NAT rules.
   150  // Returns pointer to types.VAppNetwork or error
   151  func (vapp *VApp) UpdateNetworkNatRules(networkId string, natRules []*types.NatRule, enabled bool, natType, policy string) (*types.VAppNetwork, error) {
   152  	task, err := vapp.UpdateNetworkNatRulesAsync(networkId, natRules, enabled, natType, policy)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	err = task.WaitTaskCompletion()
   157  	if err != nil {
   158  		return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err))
   159  	}
   160  
   161  	return vapp.GetVappNetworkById(networkId, false)
   162  }
   163  
   164  // UpdateNetworkNatRulesAsync asynchronously updates vApp NAT rules.
   165  // Returns task or error
   166  func (vapp *VApp) UpdateNetworkNatRulesAsync(networkId string, natRules []*types.NatRule, enabled bool, natType, policy string) (Task, error) {
   167  	util.Logger.Printf("[TRACE] UpdateNetworkNatRulesAsync with values: id: %s and natRules: %#v", networkId, natRules)
   168  
   169  	uuid := extractUuid(networkId)
   170  	networkToUpdate, err := vapp.GetVappNetworkById(uuid, true)
   171  	if err != nil {
   172  		return Task{}, err
   173  	}
   174  
   175  	if networkToUpdate.Configuration.Features == nil {
   176  		networkToUpdate.Configuration.Features = &types.NetworkFeatures{}
   177  	}
   178  	networkToUpdate.Xmlns = types.XMLNamespaceVCloud
   179  
   180  	// if services are empty return by API, then we can deduce that network isn't connected to Org network or fenced
   181  	if networkToUpdate.Configuration.Features.NatService == nil && networkToUpdate.Configuration.Features.FirewallService == nil {
   182  		return Task{}, fmt.Errorf("provided network isn't connected to org network or isn't fenced")
   183  	}
   184  	if networkToUpdate.Configuration.Features.NatService == nil {
   185  		networkToUpdate.Configuration.Features.NatService = &types.NatService{}
   186  	}
   187  	networkToUpdate.Configuration.Features.NatService.IsEnabled = enabled
   188  	networkToUpdate.Configuration.Features.NatService.NatType = natType
   189  	networkToUpdate.Configuration.Features.NatService.Policy = policy
   190  	networkToUpdate.Configuration.Features.NatService.NatRule = natRules
   191  
   192  	// here we use `PUT /network/{id}` which allow to change vApp network.
   193  	// But `GET /network/{id}` can return org VDC network or vApp network.
   194  	apiEndpoint := vapp.client.VCDHREF
   195  	apiEndpoint.Path += "/network/" + uuid
   196  
   197  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut,
   198  		types.MimeVappNetwork, "error updating vApp Network NAT rules: %s", networkToUpdate)
   199  }
   200  
   201  // RemoveAllNetworkNatRules removes all NAT rules from a vApp network
   202  // Returns error
   203  func (vapp *VApp) RemoveAllNetworkNatRules(networkId string) error {
   204  	task, err := vapp.UpdateNetworkNatRulesAsync(networkId, []*types.NatRule{}, false, "ipTranslation", "allowTraffic")
   205  	if err != nil {
   206  		return err
   207  	}
   208  	err = task.WaitTaskCompletion()
   209  	if err != nil {
   210  		return fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err))
   211  	}
   212  	return nil
   213  }
   214  
   215  // RemoveAllNetworkFirewallRules removes all network firewall rules from a vApp network.
   216  // Returns error
   217  func (vapp *VApp) RemoveAllNetworkFirewallRules(networkId string) error {
   218  	networkToUpdate, err := vapp.GetVappNetworkById(networkId, true)
   219  	if err != nil {
   220  		return err
   221  	}
   222  	task, err := vapp.UpdateNetworkFirewallRulesAsync(networkId, []*types.FirewallRule{}, false,
   223  		networkToUpdate.Configuration.Features.FirewallService.DefaultAction, networkToUpdate.Configuration.Features.FirewallService.LogDefaultAction)
   224  	if err != nil {
   225  		return err
   226  	}
   227  	err = task.WaitTaskCompletion()
   228  	if err != nil {
   229  		return fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err))
   230  	}
   231  	return nil
   232  }
   233  
   234  // UpdateNetworkStaticRouting updates vApp network static routes.
   235  // Returns pointer to types.VAppNetwork or error
   236  func (vapp *VApp) UpdateNetworkStaticRouting(networkId string, staticRoutes []*types.StaticRoute, enabled bool) (*types.VAppNetwork, error) {
   237  	task, err := vapp.UpdateNetworkStaticRoutingAsync(networkId, staticRoutes, enabled)
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  	err = task.WaitTaskCompletion()
   242  	if err != nil {
   243  		return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err))
   244  	}
   245  
   246  	return vapp.GetVappNetworkById(networkId, false)
   247  }
   248  
   249  // UpdateNetworkStaticRoutingAsync asynchronously updates vApp network static routes.
   250  // Returns task or error
   251  func (vapp *VApp) UpdateNetworkStaticRoutingAsync(networkId string, staticRoutes []*types.StaticRoute, enabled bool) (Task, error) {
   252  	util.Logger.Printf("[TRACE] UpdateNetworkStaticRoutingAsync with values: id: %s and staticRoutes: %#v, enable: %t", networkId, staticRoutes, enabled)
   253  
   254  	uuid := extractUuid(networkId)
   255  	networkToUpdate, err := vapp.GetVappNetworkById(uuid, true)
   256  	if err != nil {
   257  		return Task{}, err
   258  	}
   259  
   260  	if !IsVappNetwork(networkToUpdate.Configuration) {
   261  		return Task{}, fmt.Errorf("network static routing can be applied only for vapp network, not vapp org network")
   262  	}
   263  
   264  	if networkToUpdate.Configuration.Features == nil {
   265  		networkToUpdate.Configuration.Features = &types.NetworkFeatures{}
   266  	}
   267  	networkToUpdate.Xmlns = types.XMLNamespaceVCloud
   268  
   269  	networkToUpdate.Configuration.Features.StaticRoutingService = &types.StaticRoutingService{IsEnabled: enabled, StaticRoute: staticRoutes}
   270  
   271  	// here we use `PUT /network/{id}` which allow to change vApp network.
   272  	// But `GET /network/{id}` can return org VDC network or vApp network.
   273  	apiEndpoint := vapp.client.VCDHREF
   274  	apiEndpoint.Path += "/network/" + uuid
   275  
   276  	return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut,
   277  		types.MimeVappNetwork, "error updating vApp Network static routes: %s", networkToUpdate)
   278  }
   279  
   280  // IsVappNetwork allows to identify if given network config is a vApp network and not a vApp Org network
   281  func IsVappNetwork(networkConfig *types.NetworkConfiguration) bool {
   282  	if networkConfig.FenceMode == types.FenceModeIsolated ||
   283  		(networkConfig.FenceMode == types.FenceModeNAT && networkConfig.IPScopes != nil &&
   284  			networkConfig.IPScopes.IPScope != nil && len(networkConfig.IPScopes.IPScope) > 0 &&
   285  			!networkConfig.IPScopes.IPScope[0].IsInherited) {
   286  		return true
   287  	}
   288  	return false
   289  }
   290  
   291  // RemoveAllNetworkStaticRoutes removes all static routes from a vApp network
   292  // Returns error
   293  func (vapp *VApp) RemoveAllNetworkStaticRoutes(networkId string) error {
   294  	task, err := vapp.UpdateNetworkStaticRoutingAsync(networkId, []*types.StaticRoute{}, false)
   295  	if err != nil {
   296  		return err
   297  	}
   298  	err = task.WaitTaskCompletion()
   299  	if err != nil {
   300  		return fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err))
   301  	}
   302  	return nil
   303  }
   304  
   305  // queryVappNetworks returns a list of vApp networks with an optional filter
   306  func queryVappNetworks(client *Client, values map[string]string) ([]*types.QueryResultVappNetworkRecordType, error) {
   307  
   308  	vAppNetworkType := types.QtVappNetwork
   309  	if client.IsSysAdmin {
   310  		vAppNetworkType = types.QtAdminVappNetwork
   311  	}
   312  
   313  	params := map[string]string{
   314  		"type": vAppNetworkType,
   315  	}
   316  	filterValue := ""
   317  	if len(values) > 0 {
   318  		var filterElements []string
   319  		for k, v := range values {
   320  			item := fmt.Sprintf("%s==%s", k, v)
   321  			filterElements = append(filterElements, item)
   322  		}
   323  		filterValue = strings.Join(filterElements, ";")
   324  	}
   325  	if filterValue != "" {
   326  		params["filter"] = filterValue
   327  	}
   328  	results, err := client.cumulativeQuery(vAppNetworkType, nil, params)
   329  	if err != nil {
   330  		return nil, fmt.Errorf("error retrieving vApp networks %s", err)
   331  	}
   332  
   333  	if client.IsSysAdmin {
   334  		return results.Results.AdminVappNetworkRecord, nil
   335  	}
   336  	return results.Results.VappNetworkRecord, nil
   337  }
   338  
   339  // QueryVappNetworks returns all vApp networks visible to the client
   340  func (client *Client) QueryVappNetworks(values map[string]string) ([]*types.QueryResultVappNetworkRecordType, error) {
   341  	return queryVappNetworks(client, values)
   342  }
   343  
   344  // QueryAllVappNetworks returns all vApp networks and vApp Org Networks belonging to the current vApp
   345  func (vapp *VApp) QueryAllVappNetworks(values map[string]string) ([]*types.QueryResultVappNetworkRecordType, error) {
   346  	// Note: when querying a field that contains a UUID, the system compares only the UUIDs, even if the full field contains more than that.
   347  	allValues := map[string]string{"vApp": extractUuid(vapp.VApp.ID)}
   348  	for k, v := range values {
   349  		allValues[k] = v
   350  	}
   351  	return queryVappNetworks(vapp.client, allValues)
   352  }
   353  
   354  // QueryVappNetworks returns all vApp networks belonging to the current vApp
   355  func (vapp *VApp) QueryVappNetworks(values map[string]string) ([]*types.QueryResultVappNetworkRecordType, error) {
   356  	// Note: when querying a field that contains a UUID, the system compares only the UUIDs, even if the full field contains more than that.
   357  	allValues := map[string]string{
   358  		"vApp":     extractUuid(vapp.VApp.ID),
   359  		"isLinked": "false",
   360  	}
   361  	for k, v := range values {
   362  		allValues[k] = v
   363  	}
   364  	return queryVappNetworks(vapp.client, allValues)
   365  }
   366  
   367  // QueryVappOrgNetworks returns all vApp networks belonging to the current vApp
   368  func (vapp *VApp) QueryVappOrgNetworks(values map[string]string) ([]*types.QueryResultVappNetworkRecordType, error) {
   369  	// Note: when querying a field that contains a UUID, the system compares only the UUIDs, even if the full field contains more than that.
   370  	allValues := map[string]string{
   371  		"vApp":     extractUuid(vapp.VApp.ID),
   372  		"isLinked": "true",
   373  	}
   374  	for k, v := range values {
   375  		allValues[k] = v
   376  	}
   377  	return queryVappNetworks(vapp.client, allValues)
   378  }