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