github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/lbservicemonitor.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  	"fmt"
     9  	"net/http"
    10  
    11  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    12  )
    13  
    14  // CreateLbServiceMonitor creates a load balancer service monitor based on mandatory fields. It is a synchronous
    15  // operation. It returns created object with all fields (including ID) populated or an error.
    16  func (egw *EdgeGateway) CreateLbServiceMonitor(lbMonitorConfig *types.LbMonitor) (*types.LbMonitor, error) {
    17  	if err := validateCreateLbServiceMonitor(lbMonitorConfig, egw); err != nil {
    18  		return nil, err
    19  	}
    20  
    21  	if !egw.HasAdvancedNetworking() {
    22  		return nil, fmt.Errorf("edge gateway does not have advanced networking enabled")
    23  	}
    24  
    25  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbMonitorPath)
    26  	if err != nil {
    27  		return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
    28  	}
    29  	// We expect to get http.StatusCreated or if not an error of type types.NSXError
    30  	resp, err := egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPost, types.AnyXMLMime,
    31  		"error creating load balancer service monitor: %s", lbMonitorConfig, &types.NSXError{})
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  
    36  	// Location header should look similar to:
    37  	// Location: [/network/edges/edge-3/loadbalancer/config/monitors/monitor-5]
    38  	lbMonitorID, err := extractNsxObjectIdFromPath(resp.Header.Get("Location"))
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	readMonitor, err := egw.GetLbServiceMonitorById(lbMonitorID)
    44  	if err != nil {
    45  		return nil, fmt.Errorf("unable to retrieve monitor with ID (%s) after creation: %s", lbMonitorID, err)
    46  	}
    47  	return readMonitor, nil
    48  }
    49  
    50  // getLbServiceMonitor is able to find the types.LbMonitor type by Name and/or ID.
    51  // If both - Name and ID are specified it performs a lookup by ID and returns an error if the specified name and found
    52  // name do not match.
    53  func (egw *EdgeGateway) getLbServiceMonitor(lbMonitorConfig *types.LbMonitor) (*types.LbMonitor, error) {
    54  	if err := validateGetLbServiceMonitor(lbMonitorConfig, egw); err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	serviceMonitors, err := egw.GetLbServiceMonitors()
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	// Search for monitor by ID or by Name
    64  	for _, monitor := range serviceMonitors {
    65  		// If ID was specified for lookup - look for the same ID
    66  		if lbMonitorConfig.ID != "" && monitor.ID == lbMonitorConfig.ID {
    67  			return monitor, nil
    68  		}
    69  
    70  		// If Name was specified for lookup - look for the same Name
    71  		if lbMonitorConfig.Name != "" && monitor.Name == lbMonitorConfig.Name {
    72  			// We found it by name. Let's verify if search ID was specified and it matches the lookup object
    73  			if lbMonitorConfig.ID != "" && monitor.ID != lbMonitorConfig.ID {
    74  				return nil, fmt.Errorf("load balancer monitor was found by name (%s), but its ID (%s) does not match specified ID (%s)",
    75  					monitor.Name, monitor.ID, lbMonitorConfig.ID)
    76  			}
    77  			return monitor, nil
    78  		}
    79  	}
    80  
    81  	return nil, ErrorEntityNotFound
    82  }
    83  
    84  // GetLbServiceMonitors return all service monitors without filtering
    85  func (egw *EdgeGateway) GetLbServiceMonitors() ([]*types.LbMonitor, error) {
    86  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbMonitorPath)
    87  	if err != nil {
    88  		return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
    89  	}
    90  
    91  	// Anonymous struct to unwrap "monitor response"
    92  	lbMonitorResponse := &struct {
    93  		LBMonitors []*types.LbMonitor `xml:"monitor"`
    94  	}{}
    95  
    96  	// This query returns all service monitors as the API does not have filtering options
    97  	_, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, "unable to read Load Balancer monitor: %s", nil, lbMonitorResponse)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	return lbMonitorResponse.LBMonitors, nil
   103  }
   104  
   105  // GetLbServiceMonitorById wraps getLbServiceMonitor and needs only an ID for lookup
   106  func (egw *EdgeGateway) GetLbServiceMonitorById(id string) (*types.LbMonitor, error) {
   107  	return egw.getLbServiceMonitor(&types.LbMonitor{ID: id})
   108  }
   109  
   110  // GetLbServiceMonitorByName wraps getLbServiceMonitor and needs only a Name for lookup
   111  func (egw *EdgeGateway) GetLbServiceMonitorByName(name string) (*types.LbMonitor, error) {
   112  	return egw.getLbServiceMonitor(&types.LbMonitor{Name: name})
   113  }
   114  
   115  // UpdateLbServiceMonitor updates types.LbMonitor with all fields. At least name or ID must be specified.
   116  // If both - Name and ID are specified it performs a lookup by ID and returns an error if the specified name and found
   117  // name do not match.
   118  func (egw *EdgeGateway) UpdateLbServiceMonitor(lbMonitorConfig *types.LbMonitor) (*types.LbMonitor, error) {
   119  	err := validateUpdateLbServiceMonitor(lbMonitorConfig, egw)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	lbMonitorConfig.ID, err = egw.getLbServiceMonitorIdByNameId(lbMonitorConfig.Name, lbMonitorConfig.ID)
   125  	if err != nil {
   126  		return nil, fmt.Errorf("cannot update load balancer service monitor: %s", err)
   127  	}
   128  
   129  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbMonitorPath + lbMonitorConfig.ID)
   130  	if err != nil {
   131  		return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
   132  	}
   133  
   134  	// Result should be 204, if not we expect an error of type types.NSXError
   135  	_, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime,
   136  		"error while updating load balancer service monitor : %s", lbMonitorConfig, &types.NSXError{})
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  
   141  	readMonitor, err := egw.GetLbServiceMonitorById(lbMonitorConfig.ID)
   142  	if err != nil {
   143  		return nil, fmt.Errorf("unable to retrieve monitor with ID (%s) after update: %s", lbMonitorConfig.ID, err)
   144  	}
   145  	return readMonitor, nil
   146  }
   147  
   148  // DeleteLbServiceMonitor is able to delete the types.LbMonitor type by Name and/or ID.
   149  // If both - Name and ID are specified it performs a lookup by ID and returns an error if the specified name and found
   150  // name do not match.
   151  func (egw *EdgeGateway) DeleteLbServiceMonitor(lbMonitorConfig *types.LbMonitor) error {
   152  	err := validateDeleteLbServiceMonitor(lbMonitorConfig, egw)
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	lbMonitorConfig.ID, err = egw.getLbServiceMonitorIdByNameId(lbMonitorConfig.Name, lbMonitorConfig.ID)
   158  	if err != nil {
   159  		return fmt.Errorf("cannot delete load balancer service monitor: %s", err)
   160  	}
   161  
   162  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbMonitorPath + lbMonitorConfig.ID)
   163  	if err != nil {
   164  		return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
   165  	}
   166  
   167  	_, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodDelete, types.AnyXMLMime,
   168  		"unable to delete service monitor: %s", nil, &types.NSXError{})
   169  	if err != nil {
   170  		return err
   171  	}
   172  
   173  	return nil
   174  }
   175  
   176  // DeleteLbServiceMonitorById wraps DeleteLbServiceMonitor and requires only ID for deletion
   177  func (egw *EdgeGateway) DeleteLbServiceMonitorById(id string) error {
   178  	return egw.DeleteLbServiceMonitor(&types.LbMonitor{ID: id})
   179  }
   180  
   181  // DeleteLbServiceMonitorByName wraps DeleteLbServiceMonitor and requires only Name for deletion
   182  func (egw *EdgeGateway) DeleteLbServiceMonitorByName(name string) error {
   183  	return egw.DeleteLbServiceMonitor(&types.LbMonitor{Name: name})
   184  }
   185  
   186  func validateCreateLbServiceMonitor(lbMonitorConfig *types.LbMonitor, egw *EdgeGateway) error {
   187  	if !egw.HasAdvancedNetworking() {
   188  		return fmt.Errorf("only advanced edge gateways support load balancers")
   189  	}
   190  
   191  	if lbMonitorConfig.Name == "" {
   192  		return fmt.Errorf("load balancer monitor Name cannot be empty")
   193  	}
   194  
   195  	if lbMonitorConfig.Timeout == 0 {
   196  		return fmt.Errorf("load balancer monitor Timeout cannot be 0")
   197  	}
   198  
   199  	if lbMonitorConfig.Interval == 0 {
   200  		return fmt.Errorf("load balancer monitor Interval cannot be 0")
   201  	}
   202  
   203  	if lbMonitorConfig.MaxRetries == 0 {
   204  		return fmt.Errorf("load balancer monitor MaxRetries cannot be 0")
   205  	}
   206  
   207  	if lbMonitorConfig.Type == "" {
   208  		return fmt.Errorf("load balancer monitor Type cannot be empty")
   209  	}
   210  
   211  	return nil
   212  }
   213  
   214  func validateGetLbServiceMonitor(lbMonitorConfig *types.LbMonitor, egw *EdgeGateway) error {
   215  	if !egw.HasAdvancedNetworking() {
   216  		return fmt.Errorf("only advanced edge gateways support load balancers")
   217  	}
   218  
   219  	if lbMonitorConfig.ID == "" && lbMonitorConfig.Name == "" {
   220  		return fmt.Errorf("to read load balancer service monitor at least one of `ID`, `Name` fields must be specified")
   221  	}
   222  
   223  	return nil
   224  }
   225  
   226  func validateUpdateLbServiceMonitor(lbMonitorConfig *types.LbMonitor, egw *EdgeGateway) error {
   227  	// Update and create have the same requirements for now
   228  	return validateCreateLbServiceMonitor(lbMonitorConfig, egw)
   229  }
   230  
   231  func validateDeleteLbServiceMonitor(lbMonitorConfig *types.LbMonitor, egw *EdgeGateway) error {
   232  	// Read and delete have the same requirements for now
   233  	return validateGetLbServiceMonitor(lbMonitorConfig, egw)
   234  }
   235  
   236  // getLbServiceMonitorIdByNameId checks if at least name or ID is set and returns the ID.
   237  // If the ID is specified - it passes through the ID. If only name was specified
   238  // it will lookup the object by name and return the ID.
   239  func (egw *EdgeGateway) getLbServiceMonitorIdByNameId(name, id string) (string, error) {
   240  	if name == "" && id == "" {
   241  		return "", fmt.Errorf("at least Name or ID must be specific to find load balancer "+
   242  			"service monitor got name (%s) ID (%s)", name, id)
   243  	}
   244  	if id != "" {
   245  		return id, nil
   246  	}
   247  
   248  	// if only name was specified, ID must be found, because only ID can be used in request path
   249  	readlbServiceMonitor, err := egw.GetLbServiceMonitorByName(name)
   250  	if err != nil {
   251  		return "", fmt.Errorf("unable to find load balancer service monitor by name: %s", err)
   252  	}
   253  	return readlbServiceMonitor.ID, nil
   254  }