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 }