github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/nsxv_firewall.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  	"github.com/vmware/go-vcloud-director/v2/util"
    14  )
    15  
    16  // requestEdgeFirewallRules nests EdgeFirewallRule as a convenience for unmarshalling POST requests
    17  type requestEdgeFirewallRules struct {
    18  	XMLName           xml.Name                  `xml:"firewallRules"`
    19  	EdgeFirewallRules []*types.EdgeFirewallRule `xml:"firewallRule"`
    20  }
    21  
    22  // responseEdgeFirewallRules is used to unwrap response when retrieving
    23  type responseEdgeFirewallRules struct {
    24  	XMLName           xml.Name                 `xml:"firewall"`
    25  	Version           string                   `xml:"version"`
    26  	EdgeFirewallRules requestEdgeFirewallRules `xml:"firewallRules"`
    27  }
    28  
    29  // CreateNsxvFirewallRule creates firewall rule using proxied NSX-V API. It is a synchronous operation.
    30  // It returns an object with all fields populated (including ID)
    31  // If aboveRuleId is not empty, it will send a query parameter aboveRuleId= which instructs NSX to
    32  // place this rule above the specified rule ID
    33  func (egw *EdgeGateway) CreateNsxvFirewallRule(firewallRuleConfig *types.EdgeFirewallRule, aboveRuleId string) (*types.EdgeFirewallRule, error) {
    34  	if err := validateCreateNsxvFirewallRule(firewallRuleConfig, egw); err != nil {
    35  		return nil, err
    36  	}
    37  
    38  	params := make(map[string]string)
    39  	if aboveRuleId != "" {
    40  		params["aboveRuleId"] = aboveRuleId
    41  	}
    42  
    43  	// Wrap the provided rule for POST request
    44  	firewallRuleRequest := requestEdgeFirewallRules{
    45  		EdgeFirewallRules: []*types.EdgeFirewallRule{firewallRuleConfig},
    46  	}
    47  
    48  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateFirewallPath)
    49  	if err != nil {
    50  		return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
    51  	}
    52  	// We expect to get http.StatusCreated or if not an error of type types.NSXError
    53  	// The query must be wrapped differently, depending if it mus specify the "aboveRuleId" parameter
    54  	var resp *http.Response
    55  	if aboveRuleId == "" {
    56  		resp, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPost, types.AnyXMLMime,
    57  			"error creating firewall rule: %s", firewallRuleRequest, &types.NSXError{})
    58  	} else {
    59  		errString := fmt.Sprintf("error creating firewall rule (aboveRuleId: %s): %%s", aboveRuleId)
    60  		resp, err = egw.client.ExecuteParamRequestWithCustomError(httpPath, params, http.MethodPost, types.AnyXMLMime,
    61  			errString, firewallRuleConfig, &types.NSXError{})
    62  	}
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	// Location header should look similar to:
    68  	// [/network/edges/edge-1/firewall/config/rules/197157]
    69  	firewallRuleId, err := extractNsxObjectIdFromPath(resp.Header.Get("Location"))
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	readFirewallRule, err := egw.GetNsxvFirewallRuleById(firewallRuleId)
    75  	if err != nil {
    76  		return nil, fmt.Errorf("unable to retrieve firewall rule with ID (%s) after creation: %s",
    77  			firewallRuleId, err)
    78  	}
    79  	return readFirewallRule, nil
    80  }
    81  
    82  // UpdateNsxvFirewallRule updates types.EdgeFirewallRule with all fields using proxied NSX-V API.
    83  // Real firewall rule ID (not the number shown in UI) is mandatory to perform the update.
    84  func (egw *EdgeGateway) UpdateNsxvFirewallRule(firewallRuleConfig *types.EdgeFirewallRule) (*types.EdgeFirewallRule, error) {
    85  	err := validateUpdateNsxvFirewallRule(firewallRuleConfig, egw)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateFirewallPath + "/" + firewallRuleConfig.ID)
    91  	if err != nil {
    92  		return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
    93  	}
    94  
    95  	// Result is either 204 for success, or an error of type types.NSXError
    96  	_, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime,
    97  		"error while updating firewall rule : %s", firewallRuleConfig, &types.NSXError{})
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	readFirewallRule, err := egw.GetNsxvFirewallRuleById(firewallRuleConfig.ID)
   103  	if err != nil {
   104  		return nil, fmt.Errorf("unable to retrieve firewall rule with ID (%s) after update: %s",
   105  			readFirewallRule.ID, err)
   106  	}
   107  	return readFirewallRule, nil
   108  }
   109  
   110  // GetNsxvFirewallRuleById retrieves types.EdgeFirewallRule by real (not the number shown in UI)
   111  // firewall rule ID as shown in the UI using proxied NSX-V API.
   112  // It returns and error `ErrorEntityNotFound` if the firewall rule is not found
   113  func (egw *EdgeGateway) GetNsxvFirewallRuleById(id string) (*types.EdgeFirewallRule, error) {
   114  	if err := validateGetNsxvFirewallRule(id, egw); err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	edgeFirewallRules, err := egw.GetAllNsxvFirewallRules()
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	util.Logger.Printf("[DEBUG] Searching for firewall rule with ID: %s", id)
   124  	for _, rule := range edgeFirewallRules {
   125  		util.Logger.Printf("[DEBUG] Checking rule: %#+v", rule)
   126  		if rule.ID != "" && rule.ID == id {
   127  			return rule, nil
   128  		}
   129  	}
   130  
   131  	return nil, ErrorEntityNotFound
   132  }
   133  
   134  // GetAllNsxvFirewallRules retrieves all firewall rules and returns []*types.EdgeFirewallRule or an
   135  // error of type ErrorEntityNotFound if there are no firewall rules
   136  func (egw *EdgeGateway) GetAllNsxvFirewallRules() ([]*types.EdgeFirewallRule, error) {
   137  	if !egw.HasAdvancedNetworking() {
   138  		return nil, fmt.Errorf("only advanced edge gateways support firewall rules")
   139  	}
   140  
   141  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeFirewallPath)
   142  	if err != nil {
   143  		return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
   144  	}
   145  
   146  	firewallRuleResponse := &responseEdgeFirewallRules{}
   147  
   148  	// This query returns all application rules as the API does not have filtering options
   149  	_, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime,
   150  		"unable to read firewall rules: %s", nil, firewallRuleResponse)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	if len(firewallRuleResponse.EdgeFirewallRules.EdgeFirewallRules) == 0 {
   156  		return nil, ErrorEntityNotFound
   157  	}
   158  
   159  	return firewallRuleResponse.EdgeFirewallRules.EdgeFirewallRules, nil
   160  }
   161  
   162  // DeleteNsxvFirewallRuleById deletes types.EdgeFirewallRule by real (not the number shown in UI)
   163  // firewall rule ID as shown in the UI using proxied NSX-V API.
   164  // It returns and error `ErrorEntityNotFound` if the firewall rule is not found.
   165  func (egw *EdgeGateway) DeleteNsxvFirewallRuleById(id string) error {
   166  	err := validateDeleteNsxvFirewallRule(id, egw)
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateFirewallPath + "/" + id)
   172  	if err != nil {
   173  		return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
   174  	}
   175  
   176  	// check if the rule exists and pass back the error at it may be 'ErrorEntityNotFound'
   177  	_, err = egw.GetNsxvFirewallRuleById(id)
   178  	if err != nil {
   179  		return err
   180  	}
   181  
   182  	_, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodDelete, types.AnyXMLMime,
   183  		"unable to delete firewall rule: %s", nil, &types.NSXError{})
   184  	if err != nil {
   185  		return err
   186  	}
   187  
   188  	return nil
   189  }
   190  
   191  func validateCreateNsxvFirewallRule(firewallRuleConfig *types.EdgeFirewallRule, egw *EdgeGateway) error {
   192  	if !egw.HasAdvancedNetworking() {
   193  		return fmt.Errorf("only advanced edge gateways support firewall rules")
   194  	}
   195  
   196  	if firewallRuleConfig.Action == "" {
   197  		return fmt.Errorf("firewall rule must have action specified")
   198  	}
   199  
   200  	return nil
   201  }
   202  
   203  func validateUpdateNsxvFirewallRule(firewallRuleConfig *types.EdgeFirewallRule, egw *EdgeGateway) error {
   204  	if firewallRuleConfig.ID == "" {
   205  		return fmt.Errorf("firewall rule ID must be set for update")
   206  	}
   207  
   208  	return validateCreateNsxvFirewallRule(firewallRuleConfig, egw)
   209  }
   210  
   211  func validateGetNsxvFirewallRule(id string, egw *EdgeGateway) error {
   212  	if !egw.HasAdvancedNetworking() {
   213  		return fmt.Errorf("only advanced edge gateways support firewall rules")
   214  	}
   215  
   216  	if id == "" {
   217  		return fmt.Errorf("unable to retrieve firewall rule without ID")
   218  	}
   219  
   220  	return nil
   221  }
   222  
   223  func validateDeleteNsxvFirewallRule(id string, egw *EdgeGateway) error {
   224  	return validateGetNsxvFirewallRule(id, egw)
   225  }