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 }