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

     1  /*
     2   * Copyright 2019 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
     3   */
     4  
     5  package govcd
     6  
     7  import (
     8  	"encoding/xml"
     9  	"fmt"
    10  	"net/http"
    11  
    12  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    13  )
    14  
    15  // requestEdgeNatRules nests EdgeNatRule as a convenience for unmarshalling POST requests
    16  type requestEdgeNatRules struct {
    17  	XMLName      xml.Name             `xml:"natRules"`
    18  	EdgeNatRules []*types.EdgeNatRule `xml:"natRule"`
    19  }
    20  
    21  // responseEdgeNatRules is used to unwrap response when retrieving
    22  type responseEdgeNatRules struct {
    23  	XMLName  xml.Name            `xml:"nat"`
    24  	Version  string              `xml:"version"`
    25  	NatRules requestEdgeNatRules `xml:"natRules"`
    26  }
    27  
    28  // CreateNsxvNatRule creates NAT rule using proxied NSX-V API. It is a synchronuous operation.
    29  // It returns an object with all fields populated (including ID)
    30  func (egw *EdgeGateway) CreateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) {
    31  	if err := validateCreateNsxvNatRule(natRuleConfig, egw); err != nil {
    32  		return nil, err
    33  	}
    34  
    35  	// Wrap the provided rule for POST request
    36  	natRuleRequest := requestEdgeNatRules{
    37  		EdgeNatRules: []*types.EdgeNatRule{natRuleConfig},
    38  	}
    39  
    40  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath)
    41  	if err != nil {
    42  		return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
    43  	}
    44  	// We expect to get http.StatusCreated or if not an error of type types.NSXError
    45  	resp, err := egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPost, types.AnyXMLMime,
    46  		"error creating NAT rule: %s", natRuleRequest, &types.NSXError{})
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	// Location header should look similar to:
    52  	// [/network/edges/edge-1/nat/config/rules/197157]
    53  	natRuleId, err := extractNsxObjectIdFromPath(resp.Header.Get("Location"))
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	readNatRule, err := egw.GetNsxvNatRuleById(natRuleId)
    59  	if err != nil {
    60  		return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after creation: %s",
    61  			natRuleId, err)
    62  	}
    63  	return readNatRule, nil
    64  }
    65  
    66  // UpdateNsxvNatRule updates types.EdgeNatRule with all fields using proxied NSX-V API. ID is
    67  // mandatory to perform the update.
    68  func (egw *EdgeGateway) UpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) {
    69  	err := validateUpdateNsxvNatRule(natRuleConfig, egw)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + natRuleConfig.ID)
    75  	if err != nil {
    76  		return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
    77  	}
    78  
    79  	// Result should be 204, if not we expect an error of type types.NSXError
    80  	_, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime,
    81  		"error while updating NAT rule : %s", natRuleConfig, &types.NSXError{})
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	readNatRule, err := egw.GetNsxvNatRuleById(natRuleConfig.ID)
    87  	if err != nil {
    88  		return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after update: %s",
    89  			readNatRule.ID, err)
    90  	}
    91  	return readNatRule, nil
    92  }
    93  
    94  // GetNsxvNatRules returns a list of all NAT rules in a given edge gateway
    95  func (egw *EdgeGateway) GetNsxvNatRules() ([]*types.EdgeNatRule, error) {
    96  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeNatPath)
    97  	if err != nil {
    98  		return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
    99  	}
   100  
   101  	natRuleResponse := &responseEdgeNatRules{}
   102  
   103  	// This query returns all application rules as the API does not have filtering options
   104  	_, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime,
   105  		"unable to read NAT rule: %s", nil, natRuleResponse)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	return natRuleResponse.NatRules.EdgeNatRules, nil
   110  }
   111  
   112  // GetNsxvNatRuleById retrieves types.EdgeNatRule by NAT rule ID as shown in the UI using proxied
   113  // NSX-V API.
   114  // It returns and error `ErrorEntityNotFound` if the NAT rule is not found.
   115  func (egw *EdgeGateway) GetNsxvNatRuleById(id string) (*types.EdgeNatRule, error) {
   116  	if err := validateGetNsxvNatRule(id, egw); err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	edgeNatRules, err := egw.GetNsxvNatRules()
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	for _, rule := range edgeNatRules {
   126  		if rule.ID != "" && rule.ID == id {
   127  			return rule, nil
   128  		}
   129  	}
   130  
   131  	return nil, ErrorEntityNotFound
   132  }
   133  
   134  // DeleteNsxvNatRuleById deletes types.EdgeNatRule by NAT rule ID as shown in the UI using proxied
   135  // NSX-V API.
   136  // It returns and error `ErrorEntityNotFound` if the NAT rule is now found.
   137  func (egw *EdgeGateway) DeleteNsxvNatRuleById(id string) error {
   138  	err := validateDeleteNsxvNatRule(id, egw)
   139  	if err != nil {
   140  		return err
   141  	}
   142  
   143  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + id)
   144  	if err != nil {
   145  		return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
   146  	}
   147  
   148  	// check if the rule exists and pass back the error at it may be 'ErrorEntityNotFound'
   149  	_, err = egw.GetNsxvNatRuleById(id)
   150  	if err != nil {
   151  		return err
   152  	}
   153  
   154  	_, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodDelete, types.AnyXMLMime,
   155  		"unable to delete nat rule: %s", nil, &types.NSXError{})
   156  	if err != nil {
   157  		return err
   158  	}
   159  
   160  	return nil
   161  }
   162  
   163  func validateCreateNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error {
   164  	if !egw.HasAdvancedNetworking() {
   165  		return fmt.Errorf("only advanced edge gateways support NAT rules")
   166  	}
   167  
   168  	if natRuleConfig.Action == "" {
   169  		return fmt.Errorf("NAT rule must have an action")
   170  	}
   171  
   172  	if natRuleConfig.TranslatedAddress == "" {
   173  		return fmt.Errorf("NAT rule must translated address specified")
   174  	}
   175  
   176  	return nil
   177  }
   178  
   179  func validateUpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error {
   180  	if natRuleConfig.ID == "" {
   181  		return fmt.Errorf("NAT rule must ID must be set for update")
   182  	}
   183  
   184  	return validateCreateNsxvNatRule(natRuleConfig, egw)
   185  }
   186  
   187  func validateGetNsxvNatRule(id string, egw *EdgeGateway) error {
   188  	if !egw.HasAdvancedNetworking() {
   189  		return fmt.Errorf("only advanced edge gateways support NAT rules")
   190  	}
   191  
   192  	if id == "" {
   193  		return fmt.Errorf("unable to retrieve NAT rule without ID")
   194  	}
   195  
   196  	return nil
   197  }
   198  
   199  func validateDeleteNsxvNatRule(id string, egw *EdgeGateway) error {
   200  	return validateGetNsxvNatRule(id, egw)
   201  }