github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/lbvirtualserver.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  // CreateLbVirtualServer creates a load balancer virtual server based on mandatory fields. It is a
    15  // synchronous operation. It returns created object with all fields (including ID) populated
    16  // or an error.
    17  // Name, Protocol, Port and IpAddress fields must be populated
    18  func (egw *EdgeGateway) CreateLbVirtualServer(lbVirtualServerConfig *types.LbVirtualServer) (*types.LbVirtualServer, error) {
    19  	if err := validateCreateLbVirtualServer(lbVirtualServerConfig, egw); err != nil {
    20  		return nil, err
    21  	}
    22  
    23  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbVirtualServerPath)
    24  	if err != nil {
    25  		return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
    26  	}
    27  	// We expect to get http.StatusCreated or if not an error of type types.NSXError
    28  	resp, err := egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPost, types.AnyXMLMime,
    29  		"error creating load balancer virtual server: %s", lbVirtualServerConfig, &types.NSXError{})
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	// Location header should look similar to:
    35  	// Location: [/network/edges/edge-3/loadbalancer/config/virtualservers/virtualServer-10]
    36  	lbVirtualServerId, err := extractNsxObjectIdFromPath(resp.Header.Get("Location"))
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	readVirtualServer, err := egw.GetLbVirtualServerById(lbVirtualServerId)
    42  	if err != nil {
    43  		return nil, fmt.Errorf("unable to retrieve load balancer virtual server with ID (%s) after creation: %s",
    44  			lbVirtualServerId, err)
    45  	}
    46  	return readVirtualServer, nil
    47  }
    48  
    49  // getLbVirtualServer is able to find the types.LbVirtualServer type by Name and/or ID.
    50  // If both - Name and ID are specified it performs a lookup by ID and returns an error if the specified name and found
    51  // name do not match.
    52  func (egw *EdgeGateway) getLbVirtualServer(lbVirtualServerConfig *types.LbVirtualServer) (*types.LbVirtualServer, error) {
    53  	if err := validateGetLbVirtualServer(lbVirtualServerConfig, egw); err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	vs, err := egw.GetLbVirtualServers()
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	// Search for virtual server by ID or by Name
    63  	for _, virtualServer := range vs {
    64  		// If ID was specified for lookup - look for the same ID
    65  		if lbVirtualServerConfig.ID != "" && virtualServer.ID == lbVirtualServerConfig.ID {
    66  			return virtualServer, nil
    67  		}
    68  
    69  		// If Name was specified for lookup - look for the same Name
    70  		if lbVirtualServerConfig.Name != "" && virtualServer.Name == lbVirtualServerConfig.Name {
    71  			// We found it by name. Let's verify if search ID was specified and it matches the lookup object
    72  			if lbVirtualServerConfig.ID != "" && virtualServer.ID != lbVirtualServerConfig.ID {
    73  				return nil, fmt.Errorf("load balancer virtual server was found by name (%s), "+
    74  					"but its ID (%s) does not match specified ID (%s)",
    75  					virtualServer.Name, virtualServer.ID, lbVirtualServerConfig.ID)
    76  			}
    77  			return virtualServer, nil
    78  		}
    79  	}
    80  
    81  	return nil, ErrorEntityNotFound
    82  }
    83  
    84  // GetLbVirtualServers is getting all virtual servers without filtering anything
    85  func (egw *EdgeGateway) GetLbVirtualServers() ([]*types.LbVirtualServer, error) {
    86  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbVirtualServerPath)
    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 "virtual server response"
    92  	lbVirtualServerResponse := &struct {
    93  		LBVirtualServers []*types.LbVirtualServer `xml:"virtualServer"`
    94  	}{}
    95  
    96  	// This query returns all virtual servers as the API does not have filtering options
    97  	_, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime,
    98  		"unable to read load balancer virtual server: %s", nil, lbVirtualServerResponse)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	return lbVirtualServerResponse.LBVirtualServers, nil
   104  }
   105  
   106  // GetLbVirtualServerById wraps getLbVirtualServers and needs only an ID for lookup
   107  func (egw *EdgeGateway) GetLbVirtualServerById(id string) (*types.LbVirtualServer, error) {
   108  	return egw.getLbVirtualServer(&types.LbVirtualServer{ID: id})
   109  }
   110  
   111  // GetLbVirtualServerByName wraps getLbVirtualServers and needs only a Name for lookup
   112  func (egw *EdgeGateway) GetLbVirtualServerByName(name string) (*types.LbVirtualServer, error) {
   113  	return egw.getLbVirtualServer(&types.LbVirtualServer{Name: name})
   114  }
   115  
   116  // UpdateLbVirtualServer updates types.LbVirtualServer with all fields. At least name or ID must be
   117  // specified. If both - Name and ID are specified it performs a lookup by ID and returns an error if
   118  // the specified name and found name do not match.
   119  // Name, Protocol, Port and IpAddress fields must be populated
   120  func (egw *EdgeGateway) UpdateLbVirtualServer(lbVirtualServerConfig *types.LbVirtualServer) (*types.LbVirtualServer, error) {
   121  	err := validateUpdateLbVirtualServer(lbVirtualServerConfig, egw)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	lbVirtualServerConfig.ID, err = egw.getLbVirtualServerIdByNameId(lbVirtualServerConfig.Name, lbVirtualServerConfig.ID)
   127  	if err != nil {
   128  		return nil, fmt.Errorf("cannot update load balancer virtual server: %s", err)
   129  	}
   130  
   131  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbVirtualServerPath + lbVirtualServerConfig.ID)
   132  	if err != nil {
   133  		return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
   134  	}
   135  
   136  	// Result should be 204, if not we expect an error of type types.NSXError
   137  	_, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime,
   138  		"error while updating load balancer virtual server : %s", lbVirtualServerConfig, &types.NSXError{})
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	readVirtualServer, err := egw.GetLbVirtualServerById(lbVirtualServerConfig.ID)
   144  	if err != nil {
   145  		return nil, fmt.Errorf("unable to retrieve virtual server with ID (%s) after update: %s",
   146  			lbVirtualServerConfig.ID, err)
   147  	}
   148  	return readVirtualServer, nil
   149  }
   150  
   151  // DeleteLbVirtualServer is able to delete the types.LbVirtualServer type by Name and/or ID.
   152  // If both - Name and ID are specified it performs a lookup by ID and returns an error if the
   153  // specified name and found name do not match.
   154  func (egw *EdgeGateway) DeleteLbVirtualServer(lbVirtualServerConfig *types.LbVirtualServer) error {
   155  	err := validateDeleteLbVirtualServer(lbVirtualServerConfig, egw)
   156  	if err != nil {
   157  		return err
   158  	}
   159  
   160  	lbVirtualServerConfig.ID, err = egw.getLbVirtualServerIdByNameId(lbVirtualServerConfig.Name, lbVirtualServerConfig.ID)
   161  	if err != nil {
   162  		return fmt.Errorf("cannot delete load balancer virtual server: %s", err)
   163  	}
   164  
   165  	httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbVirtualServerPath + lbVirtualServerConfig.ID)
   166  	if err != nil {
   167  		return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err)
   168  	}
   169  
   170  	_, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodDelete, types.AnyXMLMime,
   171  		"unable to delete load balancer virtual server: %s", nil, &types.NSXError{})
   172  	if err != nil {
   173  		return err
   174  	}
   175  
   176  	return nil
   177  }
   178  
   179  // DeleteLbVirtualServerById wraps DeleteLbVirtualServer and requires only ID for deletion
   180  func (egw *EdgeGateway) DeleteLbVirtualServerById(id string) error {
   181  	return egw.DeleteLbVirtualServer(&types.LbVirtualServer{ID: id})
   182  }
   183  
   184  // DeleteLbVirtualServerByName wraps DeleteLbVirtualServer and requires only Name for deletion
   185  func (egw *EdgeGateway) DeleteLbVirtualServerByName(name string) error {
   186  	return egw.DeleteLbVirtualServer(&types.LbVirtualServer{Name: name})
   187  }
   188  
   189  func validateCreateLbVirtualServer(lbVirtualServerConfig *types.LbVirtualServer, egw *EdgeGateway) error {
   190  	if !egw.HasAdvancedNetworking() {
   191  		return fmt.Errorf("only advanced edge gateways support load balancers")
   192  	}
   193  
   194  	if lbVirtualServerConfig.Name == "" {
   195  		return fmt.Errorf("load balancer virtual server Name cannot be empty")
   196  	}
   197  
   198  	if lbVirtualServerConfig.IpAddress == "" {
   199  		return fmt.Errorf("load balancer virtual server IpAddress cannot be empty")
   200  	}
   201  
   202  	if lbVirtualServerConfig.Protocol == "" {
   203  		return fmt.Errorf("load balancer virtual server Protocol cannot be empty")
   204  	}
   205  
   206  	if lbVirtualServerConfig.Port == 0 {
   207  		return fmt.Errorf("load balancer virtual server Port cannot be empty")
   208  	}
   209  
   210  	return nil
   211  }
   212  
   213  func validateGetLbVirtualServer(lbVirtualServerConfig *types.LbVirtualServer, egw *EdgeGateway) error {
   214  	if !egw.HasAdvancedNetworking() {
   215  		return fmt.Errorf("only advanced edge gateways support load balancers")
   216  	}
   217  
   218  	if lbVirtualServerConfig.ID == "" && lbVirtualServerConfig.Name == "" {
   219  		return fmt.Errorf("to read load balancer virtual server at least one of `ID`, `Name` " +
   220  			"fields must be specified")
   221  	}
   222  
   223  	return nil
   224  }
   225  
   226  func validateUpdateLbVirtualServer(lbVirtualServerConfig *types.LbVirtualServer, egw *EdgeGateway) error {
   227  	// Update and create have the same requirements for now
   228  	return validateCreateLbVirtualServer(lbVirtualServerConfig, egw)
   229  }
   230  
   231  func validateDeleteLbVirtualServer(lbVirtualServerConfig *types.LbVirtualServer, egw *EdgeGateway) error {
   232  	// Read and delete have the same requirements for now
   233  	return validateGetLbVirtualServer(lbVirtualServerConfig, egw)
   234  }
   235  
   236  // getLbVirtualServerIdByNameId 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) getLbVirtualServerIdByNameId(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  			"virtual server 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  	readLbVirtualServer, err := egw.GetLbVirtualServerByName(name)
   250  	if err != nil {
   251  		return "", fmt.Errorf("unable to find load balancer virtual server by name: %s", err)
   252  	}
   253  	return readLbVirtualServer.ID, nil
   254  }