github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/nsxt_edgegateway.go (about) 1 /* 2 * Copyright 2020 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. 3 */ 4 5 package govcd 6 7 import ( 8 "fmt" 9 "net/netip" 10 "net/url" 11 12 "github.com/vmware/go-vcloud-director/v2/types/v56" 13 "github.com/vmware/go-vcloud-director/v2/util" 14 ) 15 16 // NsxtEdgeGateway uses OpenAPI endpoint to operate NSX-T Edge Gateways 17 type NsxtEdgeGateway struct { 18 EdgeGateway *types.OpenAPIEdgeGateway 19 client *Client 20 } 21 22 // GetNsxtEdgeGatewayById allows retrieving NSX-T edge gateway by ID for Org admins 23 func (adminOrg *AdminOrg) GetNsxtEdgeGatewayById(id string) (*NsxtEdgeGateway, error) { 24 return getNsxtEdgeGatewayById(adminOrg.client, id, nil) 25 } 26 27 // GetNsxtEdgeGatewayById allows retrieving NSX-T edge gateway by ID for Org users 28 func (org *Org) GetNsxtEdgeGatewayById(id string) (*NsxtEdgeGateway, error) { 29 return getNsxtEdgeGatewayById(org.client, id, nil) 30 } 31 32 // GetNsxtEdgeGatewayById allows retrieving NSX-T edge gateway by ID for specific VDC 33 func (vdc *Vdc) GetNsxtEdgeGatewayById(id string) (*NsxtEdgeGateway, error) { 34 params := url.Values{} 35 filterParams := queryParameterFilterAnd("ownerRef.id=="+vdc.Vdc.ID, params) 36 egw, err := getNsxtEdgeGatewayById(vdc.client, id, filterParams) 37 if err != nil { 38 return nil, err 39 } 40 41 if egw.EdgeGateway.OwnerRef.ID != vdc.Vdc.ID { 42 return nil, fmt.Errorf("%s: no NSX-T Edge Gateway with ID '%s' found in VDC '%s'", 43 ErrorEntityNotFound, id, vdc.Vdc.ID) 44 } 45 46 return egw, nil 47 } 48 49 // GetNsxtEdgeGatewayByName allows retrieving NSX-T edge gateway by Name for Org admins 50 func (adminOrg *AdminOrg) GetNsxtEdgeGatewayByName(name string) (*NsxtEdgeGateway, error) { 51 queryParameters := url.Values{} 52 queryParameters.Add("filter", "name=="+name) 53 54 allEdges, err := adminOrg.GetAllNsxtEdgeGateways(queryParameters) 55 if err != nil { 56 return nil, fmt.Errorf("unable to retrieve Edge Gateway by name '%s': %s", name, err) 57 } 58 59 onlyNsxtEdges := filterOnlyNsxtEdges(allEdges) 60 61 return returnSingleNsxtEdgeGateway(name, onlyNsxtEdges) 62 } 63 64 // GetNsxtEdgeGatewayByName allows retrieving NSX-T edge gateway by Name for Org admins 65 func (org *Org) GetNsxtEdgeGatewayByName(name string) (*NsxtEdgeGateway, error) { 66 queryParameters := url.Values{} 67 queryParameters.Add("filter", "name=="+name) 68 69 allEdges, err := org.GetAllNsxtEdgeGateways(queryParameters) 70 if err != nil { 71 return nil, fmt.Errorf("unable to retrieve Edge Gateway by name '%s': %s", name, err) 72 } 73 74 onlyNsxtEdges := filterOnlyNsxtEdges(allEdges) 75 76 return returnSingleNsxtEdgeGateway(name, onlyNsxtEdges) 77 } 78 79 // GetNsxtEdgeGatewayByNameAndOwnerId looks up NSX-T Edge Gateway by name and its owner ID (owner 80 // can be VDC or VDC Group). 81 func (org *Org) GetNsxtEdgeGatewayByNameAndOwnerId(edgeGatewayName, ownerId string) (*NsxtEdgeGateway, error) { 82 if edgeGatewayName == "" || ownerId == "" { 83 return nil, fmt.Errorf("'edgeGatewayName' and 'ownerId' must both be specified") 84 } 85 86 queryParameters := url.Values{} 87 queryParameters.Add("filter", fmt.Sprintf("ownerRef.id==%s;name==%s", ownerId, edgeGatewayName)) 88 89 allEdges, err := org.GetAllNsxtEdgeGateways(queryParameters) 90 if err != nil { 91 return nil, fmt.Errorf("unable to retrieve Edge Gateway by name '%s': %s", edgeGatewayName, err) 92 } 93 94 onlyNsxtEdges := filterOnlyNsxtEdges(allEdges) 95 96 return returnSingleNsxtEdgeGateway(edgeGatewayName, onlyNsxtEdges) 97 } 98 99 // GetNsxtEdgeGatewayByName allows to retrieve NSX-T edge gateway by Name for specific VDC 100 func (vdc *Vdc) GetNsxtEdgeGatewayByName(name string) (*NsxtEdgeGateway, error) { 101 queryParameters := url.Values{} 102 queryParameters.Add("filter", "name=="+name) 103 104 allEdges, err := vdc.GetAllNsxtEdgeGateways(queryParameters) 105 if err != nil { 106 return nil, fmt.Errorf("unable to retrieve Edge Gateway by name '%s': %s", name, err) 107 } 108 109 return returnSingleNsxtEdgeGateway(name, allEdges) 110 } 111 112 // GetNsxtEdgeGatewayByName allows to retrieve NSX-T edge gateway by Name for specific VDC Group 113 func (vdcGroup *VdcGroup) GetNsxtEdgeGatewayByName(name string) (*NsxtEdgeGateway, error) { 114 if name == "" { 115 return nil, fmt.Errorf("'name' must be specified") 116 } 117 118 queryParameters := url.Values{} 119 queryParameters.Add("filter", "name=="+name) 120 121 allEdges, err := vdcGroup.GetAllNsxtEdgeGateways(queryParameters) 122 if err != nil { 123 return nil, fmt.Errorf("unable to retrieve Edge Gateway by name '%s': %s", name, err) 124 } 125 126 return returnSingleNsxtEdgeGateway(name, allEdges) 127 } 128 129 // GetAllNsxtEdgeGateways allows to retrieve all NSX-T Edge Gateways 130 func (vcdClient *VCDClient) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeGateway, error) { 131 if vcdClient == nil { 132 return nil, fmt.Errorf("vcdClient is empty") 133 } 134 return getAllNsxtEdgeGateways(&vcdClient.Client, queryParameters) 135 } 136 137 // GetAllNsxtEdgeGateways allows to retrieve all NSX-T edge gateways for Org Admins 138 func (adminOrg *AdminOrg) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeGateway, error) { 139 return getAllNsxtEdgeGateways(adminOrg.client, queryParameters) 140 } 141 142 // GetAllNsxtEdgeGateways allows to retrieve all NSX-T edge gateways for Org users 143 func (org *Org) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeGateway, error) { 144 return getAllNsxtEdgeGateways(org.client, queryParameters) 145 } 146 147 // GetAllNsxtEdgeGateways allows to retrieve all NSX-T edge gateways for specific VDC 148 func (vdc *Vdc) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeGateway, error) { 149 filteredQueryParams := queryParameterFilterAnd("ownerRef.id=="+vdc.Vdc.ID, queryParameters) 150 return getAllNsxtEdgeGateways(vdc.client, filteredQueryParams) 151 } 152 153 // GetAllNsxtEdgeGateways allows to retrieve all NSX-T edge gateways for specific VDC 154 func (vdcGroup *VdcGroup) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeGateway, error) { 155 filteredQueryParams := queryParameterFilterAnd("ownerRef.id=="+vdcGroup.VdcGroup.Id, queryParameters) 156 return getAllNsxtEdgeGateways(vdcGroup.client, filteredQueryParams) 157 } 158 159 // CreateNsxtEdgeGateway allows to create NSX-T edge gateway for Org admins 160 func (adminOrg *AdminOrg) CreateNsxtEdgeGateway(edgeGatewayConfig *types.OpenAPIEdgeGateway) (*NsxtEdgeGateway, error) { 161 if !adminOrg.client.IsSysAdmin { 162 return nil, fmt.Errorf("only System Administrator can create Edge Gateway") 163 } 164 165 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGateways 166 minimumApiVersion, err := adminOrg.client.getOpenApiHighestElevatedVersion(endpoint) 167 if err != nil { 168 return nil, err 169 } 170 171 urlRef, err := adminOrg.client.OpenApiBuildEndpoint(endpoint) 172 if err != nil { 173 return nil, err 174 } 175 176 returnEgw := &NsxtEdgeGateway{ 177 EdgeGateway: &types.OpenAPIEdgeGateway{}, 178 client: adminOrg.client, 179 } 180 181 err = adminOrg.client.OpenApiPostItem(minimumApiVersion, urlRef, nil, edgeGatewayConfig, returnEgw.EdgeGateway, nil) 182 if err != nil { 183 return nil, fmt.Errorf("error creating Edge Gateway: %s", err) 184 } 185 186 err = returnEgw.reorderUplinks() 187 if err != nil { 188 return nil, fmt.Errorf("error reordering Edge Gateway Uplinks after update operation: %s", err) 189 } 190 191 return returnEgw, nil 192 } 193 194 // Refresh reloads NSX-T Edge Gateway contents 195 func (egw *NsxtEdgeGateway) Refresh() error { 196 if egw.EdgeGateway == nil || egw.client == nil || egw.EdgeGateway.ID == "" { 197 return fmt.Errorf("cannot refresh Edge Gateway without ID") 198 } 199 200 refreshedEdge, err := getNsxtEdgeGatewayById(egw.client, egw.EdgeGateway.ID, nil) 201 if err != nil { 202 return fmt.Errorf("error refreshing NSX-T Edge Gateway: %s", err) 203 } 204 egw.EdgeGateway = refreshedEdge.EdgeGateway 205 206 err = egw.reorderUplinks() 207 if err != nil { 208 return fmt.Errorf("error reordering Edge Gateway Uplinks after refresh operation: %s", err) 209 } 210 211 return nil 212 } 213 214 // Update allows updating NSX-T edge gateway for Org admins 215 func (egw *NsxtEdgeGateway) Update(edgeGatewayConfig *types.OpenAPIEdgeGateway) (*NsxtEdgeGateway, error) { 216 if !egw.client.IsSysAdmin { 217 return nil, fmt.Errorf("only System Administrator can update Edge Gateway") 218 } 219 220 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGateways 221 apiVersion, err := egw.client.getOpenApiHighestElevatedVersion(endpoint) 222 if err != nil { 223 return nil, err 224 } 225 226 if edgeGatewayConfig.ID == "" { 227 return nil, fmt.Errorf("cannot update Edge Gateway without ID") 228 } 229 230 urlRef, err := egw.client.OpenApiBuildEndpoint(endpoint, edgeGatewayConfig.ID) 231 if err != nil { 232 return nil, err 233 } 234 235 returnEgw := &NsxtEdgeGateway{ 236 EdgeGateway: &types.OpenAPIEdgeGateway{}, 237 client: egw.client, 238 } 239 240 err = egw.client.OpenApiPutItem(apiVersion, urlRef, nil, edgeGatewayConfig, returnEgw.EdgeGateway, nil) 241 if err != nil { 242 return nil, fmt.Errorf("error updating Edge Gateway: %s", err) 243 } 244 245 err = egw.reorderUplinks() 246 if err != nil { 247 return nil, fmt.Errorf("error reordering Edge Gateway Uplinks after update operation: %s", err) 248 } 249 250 return returnEgw, nil 251 } 252 253 // Delete allows deleting NSX-T edge gateway for sysadmins 254 func (egw *NsxtEdgeGateway) Delete() error { 255 if !egw.client.IsSysAdmin { 256 return fmt.Errorf("only Provider can delete Edge Gateway") 257 } 258 259 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGateways 260 apiVersion, err := egw.client.getOpenApiHighestElevatedVersion(endpoint) 261 if err != nil { 262 return err 263 } 264 265 if egw.EdgeGateway.ID == "" { 266 return fmt.Errorf("cannot delete Edge Gateway without ID") 267 } 268 269 urlRef, err := egw.client.OpenApiBuildEndpoint(endpoint, egw.EdgeGateway.ID) 270 if err != nil { 271 return err 272 } 273 274 err = egw.client.OpenApiDeleteItem(apiVersion, urlRef, nil, nil) 275 276 if err != nil { 277 return fmt.Errorf("error deleting Edge Gateway: %s", err) 278 } 279 280 return nil 281 } 282 283 // MoveToVdcOrVdcGroup moves NSX-T Edge Gateway to another VDC. This can cover such scenarios: 284 // * Move from VDC to VDC Group 285 // * Move from VDC Group to VDC (which is part of that VDC Group) 286 // 287 // This function is just an Update operation with OwnerRef changed to vdcGroupId, but it is more 288 // convenient to use it. 289 // Note. NSX-T Edge Gateway cannot be moved directly from one VDC to another 290 func (egw *NsxtEdgeGateway) MoveToVdcOrVdcGroup(vdcOrVdcGroupId string) (*NsxtEdgeGateway, error) { 291 edgeGatewayConfig := egw.EdgeGateway 292 edgeGatewayConfig.OwnerRef = &types.OpenApiReference{ID: vdcOrVdcGroupId} 293 // Explicitly unset VDC field because using it fails 294 edgeGatewayConfig.OrgVdc = nil 295 296 return egw.Update(edgeGatewayConfig) 297 } 298 299 // reorderUplinks will ensure that uplink at slice index 0 is the one backed by NSX-T Tier0 External network. 300 // NSX-T Edge Gateway can have many uplinks of different types (they are differentiated by 'backingType' field): 301 // * MANDATORY - exactly 1 uplink to Tier0 Gateway (External network backed by NSX-T T0 Gateway or NSX-T T0 Gateway VRF) [backingType==NSXT_TIER0 or NSXT_VRF_TIER0] 302 // * OPTIONAL - one or more External Network Uplinks (backed by NSX-T Segment backed External networks) [backingType==IMPORTED_T_LOGICAL_SWITCH] 303 // It is expected that the Tier0 gateway uplink is always at index 0, but we have seen where VCD API 304 // shuffles response values therefore it is important to ensure that uplink with 305 // backingType==NSXT_TIER0 or backingType==NSXT_VRF_TIER0 the element 0 in types.EdgeGatewayUplinks to avoid breaking functionality 306 // in upstream code. 307 // 308 // Note. This function wil be a noop in 10.4.0, because `backingType` was not present. However, this 309 // poses no risks because the can be only 1 uplink up to 10.4.1, when `backingType` was introduced. 310 func (egw *NsxtEdgeGateway) reorderUplinks() error { 311 if egw == nil || egw.EdgeGateway == nil { 312 return fmt.Errorf("edge gateway cannot be nil ") 313 } 314 315 if len(egw.EdgeGateway.EdgeGatewayUplinks) == 0 { 316 return fmt.Errorf("no uplinks present in Edge Gateway") 317 } 318 319 egw.EdgeGateway.EdgeGatewayUplinks = reorderEdgeGatewayUplinks(egw.EdgeGateway.EdgeGatewayUplinks) 320 return nil 321 } 322 323 // getNsxtEdgeGatewayById is a private parent for wrapped functions: 324 // func (adminOrg *AdminOrg) GetNsxtEdgeGatewayByName(id string) (*NsxtEdgeGateway, error) 325 // func (org *Org) GetNsxtEdgeGatewayByName(id string) (*NsxtEdgeGateway, error) 326 // func (vdc *Vdc) GetNsxtEdgeGatewayById(id string) (*NsxtEdgeGateway, error) 327 func getNsxtEdgeGatewayById(client *Client, id string, queryParameters url.Values) (*NsxtEdgeGateway, error) { 328 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGateways 329 minimumApiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) 330 if err != nil { 331 return nil, err 332 } 333 334 if id == "" { 335 return nil, fmt.Errorf("empty Edge Gateway ID") 336 } 337 338 urlRef, err := client.OpenApiBuildEndpoint(endpoint, id) 339 if err != nil { 340 return nil, err 341 } 342 343 egw := &NsxtEdgeGateway{ 344 EdgeGateway: &types.OpenAPIEdgeGateway{}, 345 client: client, 346 } 347 348 err = client.OpenApiGetItem(minimumApiVersion, urlRef, queryParameters, egw.EdgeGateway, nil) 349 if err != nil { 350 return nil, err 351 } 352 353 if egw.EdgeGateway.GatewayBacking.GatewayType != "NSXT_BACKED" { 354 return nil, fmt.Errorf("%s: this is not NSX-T Edge Gateway (%s)", 355 ErrorEntityNotFound, egw.EdgeGateway.GatewayBacking.GatewayType) 356 } 357 358 err = egw.reorderUplinks() 359 if err != nil { 360 return nil, fmt.Errorf("error reordering Edge Gateway Uplink after API retrieval") 361 } 362 363 return egw, nil 364 } 365 366 // returnSingleNsxtEdgeGateway helps to reduce code duplication for `GetNsxtEdgeGatewayByName` functions with different 367 // receivers 368 func returnSingleNsxtEdgeGateway(name string, allEdges []*NsxtEdgeGateway) (*NsxtEdgeGateway, error) { 369 if len(allEdges) > 1 { 370 return nil, fmt.Errorf("got more than 1 Edge Gateway by name '%s' %d", name, len(allEdges)) 371 } 372 373 if len(allEdges) < 1 { 374 return nil, fmt.Errorf("%s: got 0 Edge Gateways by name '%s'", ErrorEntityNotFound, name) 375 } 376 377 return allEdges[0], nil 378 } 379 380 // getAllNsxtEdgeGateways is a private parent for wrapped functions: 381 // func (adminOrg *AdminOrg) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeGateway, error) 382 // func (org *Org) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeGateway, error) 383 // func (vdc *Vdc) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeGateway, error) 384 func getAllNsxtEdgeGateways(client *Client, queryParameters url.Values) ([]*NsxtEdgeGateway, error) { 385 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGateways 386 minimumApiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) 387 if err != nil { 388 return nil, err 389 } 390 391 urlRef, err := client.OpenApiBuildEndpoint(endpoint) 392 if err != nil { 393 return nil, err 394 } 395 396 typeResponses := []*types.OpenAPIEdgeGateway{{}} 397 err = client.OpenApiGetAllItems(minimumApiVersion, urlRef, queryParameters, &typeResponses, nil) 398 if err != nil { 399 return nil, err 400 } 401 402 // Wrap all typeResponses into NsxtEdgeGateway types with client 403 wrappedResponses := make([]*NsxtEdgeGateway, len(typeResponses)) 404 for sliceIndex := range typeResponses { 405 wrappedResponses[sliceIndex] = &NsxtEdgeGateway{ 406 EdgeGateway: typeResponses[sliceIndex], 407 client: client, 408 } 409 } 410 411 onlyNsxtEdges := filterOnlyNsxtEdges(wrappedResponses) 412 413 // Reorder uplink in all Edge Gateways 414 for edgeIndex := range onlyNsxtEdges { 415 err := onlyNsxtEdges[edgeIndex].reorderUplinks() 416 if err != nil { 417 return nil, fmt.Errorf("error reordering NSX-T Edge Gateway Uplinks for gateway '%s' ('%s'): %s", 418 onlyNsxtEdges[edgeIndex].EdgeGateway.Name, onlyNsxtEdges[edgeIndex].EdgeGateway.ID, err) 419 } 420 } 421 422 return onlyNsxtEdges, nil 423 } 424 425 // filterOnlyNsxtEdges filters our list of edge gateways only for NSXT_BACKED ones because original endpoint can 426 // return NSX-V and NSX-T backed edge gateways. 427 func filterOnlyNsxtEdges(allEdges []*NsxtEdgeGateway) []*NsxtEdgeGateway { 428 filteredEdges := make([]*NsxtEdgeGateway, 0) 429 430 for index := range allEdges { 431 if allEdges[index] != nil && allEdges[index].EdgeGateway != nil && 432 allEdges[index].EdgeGateway.GatewayBacking != nil && 433 allEdges[index].EdgeGateway.GatewayBacking.GatewayType == "NSXT_BACKED" { 434 filteredEdges = append(filteredEdges, allEdges[index]) 435 } 436 } 437 438 return filteredEdges 439 } 440 441 // GetUsedIpAddresses uses dedicated endpoint to retrieve used IP addresses in an Edge Gateway 442 func (egw *NsxtEdgeGateway) GetUsedIpAddresses(queryParameters url.Values) ([]*types.GatewayUsedIpAddress, error) { 443 if egw.EdgeGateway == nil || egw.EdgeGateway.ID == "" { 444 return nil, fmt.Errorf("edge gateway ID must be set to retrieve used IP addresses") 445 } 446 client := egw.client 447 448 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayUsedIpAddresses 449 apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) 450 if err != nil { 451 return nil, err 452 } 453 454 urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID)) 455 if err != nil { 456 return nil, err 457 } 458 459 typeResponse := make([]*types.GatewayUsedIpAddress, 0) 460 err = client.OpenApiGetAllItems(apiVersion, urlRef, queryParameters, &typeResponse, nil) 461 if err != nil { 462 return nil, err 463 } 464 465 return typeResponse, nil 466 } 467 468 // GetUnusedExternalIPAddresses will retrieve a requiredIpCount of unused IP addresses for Edge 469 // Gateway 470 // Arguments: 471 // * `requiredIpCount` (how many unuseds IPs should be returned). It will fail and return an 472 // error if all IPs specified in 'requiredIpCount' cannot be found. 473 // * `optionalSubnet` is specified (CIDR notation, e.g. 192.168.1.0/24), it will look for an IP in 474 // this subnet only. 475 // * `refresh` defines if Edge Gateway structure should be retrieved with latest data before 476 // performing IP lookup operation 477 // 478 // Input and return arguments are using Go's native 'netip' package for IP addressing. This ensures 479 // correct support for IPv4 and IPv6 IPs. 480 // `netip.ParseAddr`, `netip.ParsePrefix`, `netip.Addr.String` functions can be used for conversion 481 // from/to strings 482 // 483 // This function performs below listed steps: 484 // 1. Retrieves a complete list of IPs in Edge Gateway uplinks (returns error if none are found) 485 // 2. if 'optionalSubnet' was specified - filter IP addresses to only fall into that subnet 486 // 3. Retrieves all used IP addresses in Edge Gateway using dedicated API endpoint 487 // 4. Subtracts used IP addresses from available list of IPs in uplink (optionally filtered by optionalSubnet in step 2) 488 // 5. Checks if 'requiredIpCount' criteria is met, returns error otherwise 489 // 6. Returns required amount of unused IPs (as defined in 'requiredIpCount') 490 // 491 // Notes: 492 // * This function uses Go's builtin `netip` package to avoid any string processing of IPs and 493 // supports IPv4 and IPv6 addressing. 494 // * If an unused IP is not found it will return 'netip.Addr{}' (not using *netip.Addr{} to match 495 // library semantics) and an error 496 // * It will return an error if any of uplink IP ranges End IP address is lower than Start IP 497 // address 498 func (egw *NsxtEdgeGateway) GetUnusedExternalIPAddresses(requiredIpCount int, optionalSubnet netip.Prefix, refresh bool) ([]netip.Addr, error) { 499 if refresh { 500 err := egw.Refresh() 501 if err != nil { 502 return nil, fmt.Errorf("error refreshing Edge Gateway: %s", err) 503 } 504 } 505 usedIpAddresses, err := egw.GetUsedIpAddresses(nil) 506 if err != nil { 507 return nil, fmt.Errorf("error getting used IP addresses for Edge Gateway: %s", err) 508 } 509 510 return getUnusedExternalIPAddress(egw.EdgeGateway.EdgeGatewayUplinks, usedIpAddresses, requiredIpCount, optionalSubnet) 511 } 512 513 // GetAllUnusedExternalIPAddresses will retrieve all unassigned IP addresses for Edge Gateway It is 514 // similar to GetUnusedExternalIPAddresses but returns all unused IPs instead of a specific amount 515 func (egw *NsxtEdgeGateway) GetAllUnusedExternalIPAddresses(refresh bool) ([]netip.Addr, error) { 516 if refresh { 517 err := egw.Refresh() 518 if err != nil { 519 return nil, fmt.Errorf("error refreshing Edge Gateway: %s", err) 520 } 521 } 522 usedIpAddresses, err := egw.GetUsedIpAddresses(nil) 523 if err != nil { 524 return nil, fmt.Errorf("error getting used IP addresses for Edge Gateway: %s", err) 525 } 526 527 return getAllUnusedExternalIPAddresses(egw.EdgeGateway.EdgeGatewayUplinks, usedIpAddresses, netip.Prefix{}) 528 } 529 530 // GetAllocatedIpCount traverses all subnets in Edge Gateway and returns a count of allocated IP 531 // count for each subnet in each uplink 532 func (egw *NsxtEdgeGateway) GetAllocatedIpCount(refresh bool) (int, error) { 533 if refresh { 534 err := egw.Refresh() 535 if err != nil { 536 return 0, fmt.Errorf("error refreshing Edge Gateway: %s", err) 537 } 538 } 539 540 allocatedIpCount := 0 541 542 for _, uplink := range egw.EdgeGateway.EdgeGatewayUplinks { 543 for _, subnet := range uplink.Subnets.Values { 544 if subnet.TotalIPCount != nil { 545 allocatedIpCount += *subnet.TotalIPCount 546 } 547 } 548 } 549 550 return allocatedIpCount, nil 551 } 552 553 // GetPrimaryNetworkAllocatedIpCount returns total count of allocated IPs for first NSX-T Edge 554 // Gateway uplink 555 func (egw *NsxtEdgeGateway) GetPrimaryNetworkAllocatedIpCount(refresh bool) (int, error) { 556 if refresh { 557 err := egw.Refresh() 558 if err != nil { 559 return 0, fmt.Errorf("error refreshing Edge Gateway: %s", err) 560 } 561 } 562 563 allocatedIpCount := 0 564 565 for _, subnet := range egw.EdgeGateway.EdgeGatewayUplinks[0].Subnets.Values { 566 if subnet.TotalIPCount != nil { 567 allocatedIpCount += *subnet.TotalIPCount 568 } 569 } 570 571 return allocatedIpCount, nil 572 } 573 574 // GetAllocatedIpCountByUplinkType will return a sum of allocated IPs for particular `uplinkType` 575 // `uplinkType` can be one of 'NSXT_TIER0', 'NSXT_VRF_TIER0', 'IMPORTED_T_LOGICAL_SWITCH' 576 // 577 // Note. This function is based on BackingType field and requires at least VCD 10.4.1 578 func (egw *NsxtEdgeGateway) GetAllocatedIpCountByUplinkType(refresh bool, uplinkType string) (int, error) { 579 if egw.client.APIVCDMaxVersionIs("< 37.1") { 580 return 0, fmt.Errorf("this function requires at least VCD 10.4.1 to work") 581 } 582 583 if uplinkType != "NSXT_TIER0" && 584 uplinkType != "IMPORTED_T_LOGICAL_SWITCH" && 585 uplinkType != "NSXT_VRF_TIER0" { 586 return 0, fmt.Errorf("invalid 'uplinkType', expected 'NSXT_TIER0', 'IMPORTED_T_LOGICAL_SWITCH' or 'NSXT_VRF_TIER0', got: %s", uplinkType) 587 } 588 589 if refresh { 590 err := egw.Refresh() 591 if err != nil { 592 return 0, fmt.Errorf("error refreshing Edge Gateway: %s", err) 593 } 594 } 595 596 allocatedIpCount := 0 597 598 for _, uplink := range egw.EdgeGateway.EdgeGatewayUplinks { 599 // counting IPs only for specific uplink type 600 if uplink.BackingType != nil && *uplink.BackingType != uplinkType { 601 continue 602 } 603 for _, subnet := range uplink.Subnets.Values { 604 if subnet.TotalIPCount != nil { 605 allocatedIpCount += *subnet.TotalIPCount 606 } 607 } 608 } 609 610 return allocatedIpCount, nil 611 } 612 613 // GetUsedIpAddressSlice retrieves a list of used IP addresses in an Edge Gateway and returns it 614 // using native Go type '[]netip.Addr' 615 func (egw *NsxtEdgeGateway) GetUsedIpAddressSlice(refresh bool) ([]netip.Addr, error) { 616 if refresh { 617 err := egw.Refresh() 618 if err != nil { 619 return nil, fmt.Errorf("error refreshing Edge Gateway: %s", err) 620 } 621 } 622 usedIpAddresses, err := egw.GetUsedIpAddresses(nil) 623 if err != nil { 624 return nil, fmt.Errorf("error getting used IP addresses for Edge Gateway: %s", err) 625 } 626 627 return flattenGatewayUsedIpAddressesToIpSlice(usedIpAddresses) 628 } 629 630 // QuickDeallocateIpCount refreshes Edge Gateway structure and deallocates specified ipCount from it 631 // by modifying Uplink structure and calling Update() on it. 632 // 633 // Notes: 634 // * This is a reverse operation to QuickAllocateIpCount and is provided for convenience as the API 635 // does not support negative values for QuickAddAllocatedIPCount field 636 // * This function modifies Edge Gateway structure and calls update. To only modify structure, 637 // please use `NsxtEdgeGateway.DeallocateIpCount` function 638 func (egw *NsxtEdgeGateway) QuickDeallocateIpCount(ipCount int) (*NsxtEdgeGateway, error) { 639 if egw.EdgeGateway == nil { 640 return nil, fmt.Errorf("edge gateway is not initialized") 641 } 642 643 err := egw.Refresh() 644 if err != nil { 645 return nil, fmt.Errorf("error refreshing Edge Gateway: %s", err) 646 } 647 648 err = egw.DeallocateIpCount(ipCount) 649 if err != nil { 650 return nil, fmt.Errorf("error deallocating IP count: %s", err) 651 } 652 653 return egw.Update(egw.EdgeGateway) 654 } 655 656 // DeallocateIpCount modifies the structure to deallocate IP addresses from the Edge Gateway 657 // uplinks. 658 // 659 // Notes: 660 // * This function does not call Update() on the Edge Gateway and it is up to the caller to perform 661 // this operation (or use NsxtEdgeGateway.QuickDeallocateIpCount which wraps this function and 662 // performs API call) 663 // * Use `QuickAddAllocatedIPCount` field in the uplink structure to leverage VCD API directly for 664 // allocating IP addresses. 665 func (egw *NsxtEdgeGateway) DeallocateIpCount(deallocateIpCount int) error { 666 if deallocateIpCount < 0 { 667 return fmt.Errorf("deallocateIpCount must be greater than 0") 668 } 669 670 if egw == nil || egw.EdgeGateway == nil { 671 return fmt.Errorf("edge gateway structure cannot be nil") 672 } 673 674 edgeGatewayType := egw.EdgeGateway 675 676 for uplinkIndex, uplink := range edgeGatewayType.EdgeGatewayUplinks { 677 for subnetIndex, subnet := range uplink.Subnets.Values { 678 679 // TotalIPCount is an address of a variable so it needs to be dereferenced for easier arithmetic 680 // operations. In the end of processing the value is set back to the original location. 681 singleSubnetTotalIpCount := *edgeGatewayType.EdgeGatewayUplinks[uplinkIndex].Subnets.Values[subnetIndex].TotalIPCount 682 683 if singleSubnetTotalIpCount > 0 { 684 util.Logger.Printf("[DEBUG] Edge Gateway deallocating IPs from subnet '%s', TotalIPCount '%d', deallocate IP count '%d'", 685 subnet.Gateway, subnet.TotalIPCount, deallocateIpCount) 686 687 // If a subnet contains more allocated IPs than we need to deallocate - deallocate only what we need 688 if singleSubnetTotalIpCount >= deallocateIpCount { 689 singleSubnetTotalIpCount -= deallocateIpCount 690 691 // To make deallocation work one must set this to true 692 edgeGatewayType.EdgeGatewayUplinks[uplinkIndex].Subnets.Values[subnetIndex].AutoAllocateIPRanges = true 693 694 deallocateIpCount = 0 695 } else { // If we have less IPs allocated than we need to deallocate - deallocate all of them 696 deallocateIpCount -= singleSubnetTotalIpCount 697 singleSubnetTotalIpCount = 0 698 edgeGatewayType.EdgeGatewayUplinks[uplinkIndex].Subnets.Values[subnetIndex].AutoAllocateIPRanges = true // To make deallocation work one must set this to true 699 util.Logger.Printf("[DEBUG] Edge Gateway IP count after partial deallocation %d", edgeGatewayType.EdgeGatewayUplinks[uplinkIndex].Subnets.Values[subnetIndex].TotalIPCount) 700 } 701 } 702 703 // Setting value back to original location after all operations 704 edgeGatewayType.EdgeGatewayUplinks[uplinkIndex].Subnets.Values[subnetIndex].TotalIPCount = &singleSubnetTotalIpCount 705 util.Logger.Printf("[DEBUG] Edge Gateway IP count after complete deallocation %d", edgeGatewayType.EdgeGatewayUplinks[uplinkIndex].Subnets.Values[subnetIndex].TotalIPCount) 706 707 if deallocateIpCount == 0 { 708 break 709 } 710 } 711 } 712 713 if deallocateIpCount > 0 { 714 return fmt.Errorf("not enough IPs allocated to deallocate requested '%d' IPs", deallocateIpCount) 715 } 716 717 return nil 718 } 719 720 // GetQoS retrieves QoS (rate limiting) configuration for an NSX-T Edge Gateway 721 func (egw *NsxtEdgeGateway) GetQoS() (*types.NsxtEdgeGatewayQos, error) { 722 if egw.EdgeGateway == nil || egw.client == nil || egw.EdgeGateway.ID == "" { 723 return nil, fmt.Errorf("cannot get QoS for NSX-T Edge Gateway without ID") 724 } 725 726 client := egw.client 727 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayQos 728 apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) 729 if err != nil { 730 return nil, err 731 } 732 733 urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID)) 734 if err != nil { 735 return nil, err 736 } 737 738 qos := &types.NsxtEdgeGatewayQos{} 739 err = client.OpenApiGetItem(apiVersion, urlRef, nil, qos, nil) 740 if err != nil { 741 return nil, err 742 } 743 744 return qos, nil 745 } 746 747 // UpdateQoS updates QoS (rate limiting) configuration for an NSX-T Edge Gateway 748 func (egw *NsxtEdgeGateway) UpdateQoS(qosConfig *types.NsxtEdgeGatewayQos) (*types.NsxtEdgeGatewayQos, error) { 749 if egw.EdgeGateway == nil || egw.client == nil || egw.EdgeGateway.ID == "" { 750 return nil, fmt.Errorf("cannot update QoS for NSX-T Edge Gateway without ID") 751 } 752 753 client := egw.client 754 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayQos 755 apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) 756 if err != nil { 757 return nil, err 758 } 759 760 urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID)) 761 if err != nil { 762 return nil, err 763 } 764 765 // update QoS with given qosConfig 766 updatedQos := &types.NsxtEdgeGatewayQos{} 767 err = client.OpenApiPutItem(apiVersion, urlRef, nil, qosConfig, updatedQos, nil) 768 if err != nil { 769 return nil, err 770 } 771 772 return updatedQos, nil 773 } 774 775 // GetDhcpForwarder gets DHCP forwarder configuration for an NSX-T Edge Gateway 776 func (egw *NsxtEdgeGateway) GetDhcpForwarder() (*types.NsxtEdgeGatewayDhcpForwarder, error) { 777 if egw.EdgeGateway == nil || egw.client == nil || egw.EdgeGateway.ID == "" { 778 return nil, fmt.Errorf("cannot get DHCP forwarder for NSX-T Edge Gateway without ID") 779 } 780 781 client := egw.client 782 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayDhcpForwarder 783 apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) 784 if err != nil { 785 return nil, err 786 } 787 788 urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID)) 789 if err != nil { 790 return nil, err 791 } 792 793 dhcpForwarder := &types.NsxtEdgeGatewayDhcpForwarder{} 794 err = client.OpenApiGetItem(apiVersion, urlRef, nil, dhcpForwarder, nil) 795 if err != nil { 796 return nil, err 797 } 798 799 return dhcpForwarder, nil 800 } 801 802 // UpdateDhcpForwarder updates DHCP forwarder configuration for an NSX-T Edge Gateway 803 func (egw *NsxtEdgeGateway) UpdateDhcpForwarder(dhcpForwarderConfig *types.NsxtEdgeGatewayDhcpForwarder) (*types.NsxtEdgeGatewayDhcpForwarder, error) { 804 if egw.EdgeGateway == nil || egw.client == nil || egw.EdgeGateway.ID == "" { 805 return nil, fmt.Errorf("cannot update DHCP forwarder for NSX-T Edge Gateway without ID") 806 } 807 808 client := egw.client 809 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayDhcpForwarder 810 apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) 811 if err != nil { 812 return nil, err 813 } 814 815 urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID)) 816 if err != nil { 817 return nil, err 818 } 819 820 // update DHCP forwarder with given dhcpForwarderConfig 821 updatedDhcpForwarder, err := egw.GetDhcpForwarder() 822 if err != nil { 823 return nil, err 824 } 825 dhcpForwarderConfig.Version = updatedDhcpForwarder.Version 826 827 err = client.OpenApiPutItem(apiVersion, urlRef, nil, dhcpForwarderConfig, updatedDhcpForwarder, nil) 828 if err != nil { 829 return nil, err 830 } 831 832 return updatedDhcpForwarder, nil 833 } 834 835 // GetSlaacProfile gets SLAAC (Stateless Address Autoconfiguration) Profile configuration for an 836 // NSX-T Edge Gateway. 837 // Note. It represents DHCPv6 Edge Gateway configuration in UI 838 func (egw *NsxtEdgeGateway) GetSlaacProfile() (*types.NsxtEdgeGatewaySlaacProfile, error) { 839 if egw.EdgeGateway == nil || egw.client == nil || egw.EdgeGateway.ID == "" { 840 return nil, fmt.Errorf("cannot get SLAAC Profile for NSX-T Edge Gateway without ID") 841 } 842 843 client := egw.client 844 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewaySlaacProfile 845 apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) 846 if err != nil { 847 return nil, err 848 } 849 850 urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID)) 851 if err != nil { 852 return nil, err 853 } 854 855 slaacProfile := &types.NsxtEdgeGatewaySlaacProfile{} 856 err = client.OpenApiGetItem(apiVersion, urlRef, nil, slaacProfile, nil) 857 if err != nil { 858 return nil, err 859 } 860 861 return slaacProfile, nil 862 } 863 864 // UpdateSlaacProfile creates a SLAAC (Stateless Address Autoconfiguration) profile or updates the 865 // existing one if it already exists. 866 // Note. It represents DHCPv6 Edge Gateway configuration in UI 867 func (egw *NsxtEdgeGateway) UpdateSlaacProfile(slaacProfileConfig *types.NsxtEdgeGatewaySlaacProfile) (*types.NsxtEdgeGatewaySlaacProfile, error) { 868 if egw.EdgeGateway == nil || egw.client == nil || egw.EdgeGateway.ID == "" { 869 return nil, fmt.Errorf("cannot update SLAAC Profile for NSX-T Edge Gateway without ID") 870 } 871 872 client := egw.client 873 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewaySlaacProfile 874 apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) 875 if err != nil { 876 return nil, err 877 } 878 879 urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID)) 880 if err != nil { 881 return nil, err 882 } 883 884 updatedSlaacProfile := &types.NsxtEdgeGatewaySlaacProfile{} 885 err = client.OpenApiPutItem(apiVersion, urlRef, nil, slaacProfileConfig, updatedSlaacProfile, nil) 886 if err != nil { 887 return nil, err 888 } 889 890 return updatedSlaacProfile, nil 891 } 892 893 func getAllUnusedExternalIPAddresses(uplinks []types.EdgeGatewayUplinks, usedIpAddresses []*types.GatewayUsedIpAddress, optionalSubnet netip.Prefix) ([]netip.Addr, error) { 894 // 1. Flatten all IP ranges in Edge Gateway using Go's native 'netip.Addr' IP container instead 895 // of plain strings because it is more robust (supports IPv4 and IPv6 and also comparison 896 // operator) 897 assignedIpSlice, err := flattenEdgeGatewayUplinkToIpSlice(uplinks) 898 if err != nil { 899 return nil, fmt.Errorf("error listing all IPs in Edge Gateway: %s", err) 900 } 901 902 if len(assignedIpSlice) == 0 { 903 return nil, fmt.Errorf("no IPs found in Edge Gateway configuration") 904 } 905 906 // 2. Optionally filter given IP ranges by optionalSubnet value (if specified) 907 if optionalSubnet != (netip.Prefix{}) { 908 assignedIpSlice, err = filterIpSlicesBySubnet(assignedIpSlice, optionalSubnet) 909 if err != nil { 910 return nil, fmt.Errorf("error filtering ranges for given subnet '%s': %s", optionalSubnet, err) 911 } 912 } 913 914 // 3. Get Used IP addresses in Edge Gateway in the same slice format 915 usedIpSlice, err := flattenGatewayUsedIpAddressesToIpSlice(usedIpAddresses) 916 if err != nil { 917 return nil, fmt.Errorf("could not flatten Edge Gateway used IP addresses: %s", err) 918 } 919 920 // 4. Get all unused IPs 921 // (allIPs - allUsedIPs) = allUnusedIPs 922 unusedIps := ipSliceDifference(assignedIpSlice, usedIpSlice) 923 924 return unusedIps, nil 925 } 926 927 func getUnusedExternalIPAddress(uplinks []types.EdgeGatewayUplinks, usedIpAddresses []*types.GatewayUsedIpAddress, requiredIpCount int, optionalSubnet netip.Prefix) ([]netip.Addr, error) { 928 unusedIps, err := getAllUnusedExternalIPAddresses(uplinks, usedIpAddresses, optionalSubnet) 929 if err != nil { 930 return nil, fmt.Errorf("error getting all unused IPs: %s", err) 931 } 932 933 // 5. Check if 'requiredIpCount' criteria is met 934 if len(unusedIps) < requiredIpCount { 935 return nil, fmt.Errorf("not enough unused IPs found. Expected %d, got %d", requiredIpCount, len(unusedIps)) 936 } 937 938 // 6. Return required amount of unused IPs 939 return unusedIps[:requiredIpCount], nil 940 } 941 942 // flattenEdgeGatewayUplinkToIpSlice processes Edge Gateway Uplink structure and creates a slice of 943 // all available IPs 944 func flattenEdgeGatewayUplinkToIpSlice(uplinks []types.EdgeGatewayUplinks) ([]netip.Addr, error) { 945 assignedIpSlice := make([]netip.Addr, 0) 946 947 for _, edgeGatewayUplink := range uplinks { 948 for _, edgeGatewayUplinkSubnet := range edgeGatewayUplink.Subnets.Values { 949 for _, r := range edgeGatewayUplinkSubnet.IPRanges.Values { 950 // Convert IPs to netip.Addr 951 startIp, err := netip.ParseAddr(r.StartAddress) 952 if err != nil { 953 return nil, fmt.Errorf("error parsing start IP address in range '%s': %s", r.StartAddress, err) 954 } 955 956 // if we have end address specified - a range of IPs must be expanded into slice 957 // with all IPs in that range 958 if r.EndAddress != "" { 959 endIp, err := netip.ParseAddr(r.EndAddress) 960 if err != nil { 961 return nil, fmt.Errorf("error parsing end IP address in range '%s': %s", r.EndAddress, err) 962 } 963 964 // Check if EndAddress is lower than StartAddress ant report an error if so 965 if endIp.Less(startIp) { 966 return nil, fmt.Errorf("end IP is lower that start IP (%s < %s)", r.EndAddress, r.StartAddress) 967 } 968 969 // loop over IPs in range from startIp to endIp and add them to the slice one by one 970 // Expression 'ip.Compare(endIp) == 1' means that 'ip > endIp' and the loop should stop 971 for ip := startIp; ip.Compare(endIp) != 1; ip = ip.Next() { 972 assignedIpSlice = append(assignedIpSlice, ip) 973 } 974 } else { // if there is no end address in the range, then it is only a single IP - startIp 975 assignedIpSlice = append(assignedIpSlice, startIp) 976 } 977 } 978 } 979 } 980 981 return assignedIpSlice, nil 982 } 983 984 // ipSliceDifference performs mathematical subtraction for two slices of IPs 985 // The formula is (minuend − subtrahend = difference) 986 // 987 // Special behavior: 988 // * Passing nil minuend results in nil 989 // * Passing nil subtrahend will return minuendSlice 990 // 991 // NOTE. This function will mutate minuendSlice to save memory and avoid having a copy of all values 992 // which can become expensive if there are a lot of items 993 func ipSliceDifference(minuendSlice, subtrahendSlice []netip.Addr) []netip.Addr { 994 if minuendSlice == nil { 995 return nil 996 } 997 998 if subtrahendSlice == nil { 999 return minuendSlice 1000 } 1001 1002 // Removal of elements from an empty slice results in an empty slice 1003 if len(minuendSlice) == 0 { 1004 return []netip.Addr{} 1005 } 1006 // Having an empty subtrahendSlice results in minuendSlice 1007 if len(subtrahendSlice) == 0 { 1008 return minuendSlice 1009 } 1010 1011 resultIpCount := 0 // count of IPs after removing items from subtrahendSlice 1012 1013 // Loop over minuend IPs 1014 for _, minuendIp := range minuendSlice { 1015 1016 // Check if subtrahend has minuend element listed 1017 var foundSubtrahend bool 1018 for _, subtrahendIp := range subtrahendSlice { 1019 if subtrahendIp == minuendIp { 1020 // IP found in subtrahend, therefore breaking inner loop early 1021 foundSubtrahend = true 1022 break 1023 } 1024 } 1025 1026 // Store the IP in `minuendSlice` at `resultIpCount` index and increment the index itself 1027 if !foundSubtrahend { 1028 // Add IP to the 'resultIpCount' index position 1029 minuendSlice[resultIpCount] = minuendIp 1030 resultIpCount++ 1031 } 1032 } 1033 1034 // if all elements are removed - return nil 1035 if resultIpCount == 0 { 1036 return nil 1037 } 1038 1039 // cut off all values, greater than `resultIpCount` 1040 minuendSlice = minuendSlice[:resultIpCount] 1041 1042 return minuendSlice 1043 } 1044 1045 // filterIpSlicesBySubnet accepts 'ipRange' and returns a slice of IPs only that fall into given 1046 // 'subnet' leaving everything out 1047 // 1048 // Special behavior: 1049 // * Passing empty 'subnet' will return `nil` and an error 1050 // * Passing empty 'ipRange' will return 'nil' and an error 1051 // 1052 // Note. This function does not enforce uniqueness of IPs in 'ipRange' and if there are duplicate 1053 // IPs matching 'subnet' they will be in the resulting slice 1054 func filterIpSlicesBySubnet(ipRange []netip.Addr, subnet netip.Prefix) ([]netip.Addr, error) { 1055 if subnet == (netip.Prefix{}) { 1056 return nil, fmt.Errorf("empty subnet specified") 1057 } 1058 1059 if len(ipRange) == 0 { 1060 return nil, fmt.Errorf("empty IP Range specified") 1061 } 1062 1063 filteredRange := make([]netip.Addr, 0) 1064 1065 for _, ip := range ipRange { 1066 if subnet.Contains(ip) { 1067 filteredRange = append(filteredRange, ip) 1068 } 1069 } 1070 1071 return filteredRange, nil 1072 } 1073 1074 // flattenGatewayUsedIpAddressesToIpSlice accepts a slice of `GatewayUsedIpAddress` coming directly 1075 // from the API and converts it to slice of Go's native '[]netip.Addr' which supports IPv4 and IPv6 1076 func flattenGatewayUsedIpAddressesToIpSlice(usedIpAddresses []*types.GatewayUsedIpAddress) ([]netip.Addr, error) { 1077 usedIpSlice := make([]netip.Addr, len(usedIpAddresses)) 1078 for usedIpIndex := range usedIpAddresses { 1079 ip, err := netip.ParseAddr(usedIpAddresses[usedIpIndex].IPAddress) 1080 if err != nil { 1081 return nil, fmt.Errorf("error parsing IP '%s' in Edge Gateway used IP address list: %s", usedIpAddresses[usedIpIndex].IPAddress, err) 1082 } 1083 usedIpSlice[usedIpIndex] = ip 1084 } 1085 1086 return usedIpSlice, nil 1087 } 1088 1089 func reorderEdgeGatewayUplinks(edgeGatewayUplinks []types.EdgeGatewayUplinks) []types.EdgeGatewayUplinks { 1090 // If only 1 uplink is present - there is nothing to reorder, because only mandatory uplink is present 1091 if len(edgeGatewayUplinks) == 1 { 1092 return edgeGatewayUplinks 1093 } 1094 1095 // Element 0 is External Network backed by Tier 0 Gateway or T0 Gateway VRF - nothing to do 1096 if edgeGatewayUplinks[0].BackingType != nil && (*edgeGatewayUplinks[0].BackingType == "NSXT_TIER0" || *edgeGatewayUplinks[0].BackingType == "NSXT_VRF_TIER0") { 1097 return edgeGatewayUplinks 1098 } 1099 1100 for uplinkIndex := range edgeGatewayUplinks { 1101 if edgeGatewayUplinks[uplinkIndex].BackingType != nil && (*edgeGatewayUplinks[uplinkIndex].BackingType == "NSXT_TIER0" || *edgeGatewayUplinks[uplinkIndex].BackingType == "NSXT_VRF_TIER0") { 1102 // Swap elements so that 'NSXT_TIER0' or 'NSXT_VRF_TIER0' is at position 0 1103 t0BackedUplink := edgeGatewayUplinks[uplinkIndex] 1104 edgeGatewayUplinks[uplinkIndex] = edgeGatewayUplinks[0] 1105 edgeGatewayUplinks[0] = t0BackedUplink 1106 1107 return edgeGatewayUplinks 1108 } 1109 } 1110 1111 return edgeGatewayUplinks 1112 }