github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/edgegateway.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 "bytes" 9 "crypto/rand" 10 "encoding/xml" 11 "fmt" 12 "net/http" 13 "net/url" 14 "regexp" 15 "strconv" 16 "strings" 17 "time" 18 19 "github.com/vmware/go-vcloud-director/v2/types/v56" 20 "github.com/vmware/go-vcloud-director/v2/util" 21 ) 22 23 type EdgeGateway struct { 24 EdgeGateway *types.EdgeGateway 25 client *Client 26 } 27 28 // Simplified structure used to list networks connected to an edge gateway 29 type SimpleNetworkIdentifier struct { 30 Name string 31 InterfaceType string 32 } 33 34 var reErrorBusy = regexp.MustCompile(`is busy completing an operation.$`) 35 36 func NewEdgeGateway(cli *Client) *EdgeGateway { 37 return &EdgeGateway{ 38 EdgeGateway: new(types.EdgeGateway), 39 client: cli, 40 } 41 } 42 43 // Struct which covers NAT rule fields 44 type NatRule struct { 45 NatType string 46 NetworkHref string 47 ExternalIP string 48 ExternalPort string 49 InternalIP string 50 InternalPort string 51 Protocol string 52 IcmpSubType string 53 Description string 54 } 55 56 // AddDhcpPool adds (or updates) the DHCP pool connected to a specific network. 57 // TODO: this is legacy code from 2015, which requires a Terraform structure to work. It may need some re-thinking. 58 func (egw *EdgeGateway) AddDhcpPool(network *types.OrgVDCNetwork, dhcppool []interface{}) (Task, error) { 59 newEdgeConfig := egw.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration 60 util.Logger.Printf("[DEBUG] EDGE GATEWAY: %#v", newEdgeConfig) 61 util.Logger.Printf("[DEBUG] EDGE GATEWAY SERVICE: %#v", newEdgeConfig.GatewayDhcpService) 62 newDchpService := &types.GatewayDhcpService{} 63 if newEdgeConfig.GatewayDhcpService.Pool == nil { 64 newDchpService.IsEnabled = true 65 } else { 66 newDchpService.IsEnabled = newEdgeConfig.GatewayDhcpService.IsEnabled 67 68 for _, dhcpPoolService := range newEdgeConfig.GatewayDhcpService.Pool { 69 70 // Kludgy IF to avoid deleting DNAT rules not created by us. 71 // If matches, let's skip it and continue the loop 72 // Note: a simple comparison of HREF fields may fail if one of them is 73 // from a tenant object and the other from a provider object. They may have the 74 // same ID but different paths. Using 'equalIds' we determine equality even with 75 // different paths 76 if equalIds(network.HREF, "", dhcpPoolService.Network.HREF) { 77 continue 78 } 79 80 newDchpService.Pool = append(newDchpService.Pool, dhcpPoolService) 81 } 82 } 83 84 for _, item := range dhcppool { 85 data := item.(map[string]interface{}) 86 87 if data["default_lease_time"] == nil { 88 data["default_lease_time"] = 3600 89 } 90 91 if data["max_lease_time"] == nil { 92 data["max_lease_time"] = 7200 93 } 94 95 dhcpRule := &types.DhcpPoolService{ 96 IsEnabled: true, 97 Network: &types.Reference{ 98 HREF: network.HREF, 99 Name: network.Name, 100 }, 101 DefaultLeaseTime: data["default_lease_time"].(int), 102 MaxLeaseTime: data["max_lease_time"].(int), 103 LowIPAddress: data["start_address"].(string), 104 HighIPAddress: data["end_address"].(string), 105 } 106 newDchpService.Pool = append(newDchpService.Pool, dhcpRule) 107 } 108 109 newRules := &types.EdgeGatewayServiceConfiguration{ 110 Xmlns: types.XMLNamespaceVCloud, 111 GatewayDhcpService: newDchpService, 112 } 113 114 output, err := xml.MarshalIndent(newRules, " ", " ") 115 if err != nil { 116 return Task{}, fmt.Errorf("error reconfiguring Edge Gateway: %s", err) 117 } 118 119 var resp *http.Response 120 for { 121 buffer := bytes.NewBufferString(xml.Header + string(output)) 122 123 apiEndpoint := urlParseRequestURI(egw.EdgeGateway.HREF) 124 apiEndpoint.Path += "/action/configureServices" 125 126 req := egw.client.NewRequest(map[string]string{}, http.MethodPost, *apiEndpoint, buffer) 127 util.Logger.Printf("[DEBUG] POSTING TO URL: %s", apiEndpoint.Path) 128 util.Logger.Printf("[DEBUG] XML TO SEND:\n%s", buffer) 129 130 req.Header.Add("Content-Type", "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml") 131 132 resp, err = checkResp(egw.client.Http.Do(req)) 133 if err != nil { 134 if reErrorBusy.MatchString(err.Error()) { 135 time.Sleep(3 * time.Second) 136 continue 137 } 138 return Task{}, fmt.Errorf("error reconfiguring Edge Gateway: %s", err) 139 } 140 break 141 } 142 143 task := NewTask(egw.client) 144 145 if err = decodeBody(types.BodyTypeXML, resp, task.Task); err != nil { 146 return Task{}, fmt.Errorf("error decoding Task response: %s", err) 147 } 148 149 // The request was successful 150 return *task, nil 151 152 } 153 154 // Deprecated: use one of RemoveNATRuleAsync, RemoveNATRule 155 func (egw *EdgeGateway) RemoveNATMapping(natType, externalIP, internalIP, port string) (Task, error) { 156 return egw.RemoveNATPortMapping(natType, externalIP, port, internalIP, port) 157 } 158 159 // Deprecated: use one of RemoveNATRuleAsync, RemoveNATRule 160 func (egw *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort, internalIP, internalPort string) (Task, error) { 161 // Find uplink interface 162 var uplink types.Reference 163 for _, gi := range egw.EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface { 164 if gi.InterfaceType != "uplink" { 165 continue 166 } 167 uplink = *gi.Network 168 } 169 170 newEdgeConfig := egw.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration 171 172 // Take care of the NAT service 173 newNatService := &types.NatService{} 174 175 newNatService.IsEnabled = newEdgeConfig.NatService.IsEnabled 176 newNatService.NatType = newEdgeConfig.NatService.NatType 177 newNatService.Policy = newEdgeConfig.NatService.Policy 178 newNatService.ExternalIP = newEdgeConfig.NatService.ExternalIP 179 180 for _, natRule := range newEdgeConfig.NatService.NatRule { 181 182 if natRule.RuleType == natType && 183 natRule.GatewayNatRule.OriginalIP == externalIP && 184 natRule.GatewayNatRule.OriginalPort == externalPort && 185 natRule.GatewayNatRule.Interface.HREF == uplink.HREF { 186 util.Logger.Printf("[DEBUG] REMOVING %s Rule: %#v", natRule.RuleType, natRule.GatewayNatRule) 187 continue 188 } 189 util.Logger.Printf("[DEBUG] KEEPING %s Rule: %#v", natRule.RuleType, natRule.GatewayNatRule) 190 newNatService.NatRule = append(newNatService.NatRule, natRule) 191 } 192 193 newEdgeConfig.NatService = newNatService 194 195 newRules := &types.EdgeGatewayServiceConfiguration{ 196 Xmlns: types.XMLNamespaceVCloud, 197 NatService: newNatService, 198 } 199 200 apiEndpoint := urlParseRequestURI(egw.EdgeGateway.HREF) 201 apiEndpoint.Path += "/action/configureServices" 202 203 // Return the task 204 return egw.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, 205 "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) 206 207 } 208 209 // RemoveNATRule removes NAT removes NAT rule identified by ID and handles task. Returns error if issues rise. 210 // Old functions RemoveNATPortMapping and RemoveNATMapping removed using rule details 211 // and expected interface to be of external network type. 212 func (egw *EdgeGateway) RemoveNATRule(id string) error { 213 task, err := egw.RemoveNATRuleAsync(id) 214 if err != nil { 215 return fmt.Errorf("error removing DNAT rule: %s", err) 216 } 217 err = task.WaitTaskCompletion() 218 if err != nil { 219 return fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) 220 } 221 222 return nil 223 } 224 225 // RemoveNATRuleAsync removes NAT rule or returns an error. 226 // Old functions RemoveNATPortMapping and RemoveNATMapping removed using rule details 227 // and expected interface to be of external network type. 228 func (egw *EdgeGateway) RemoveNATRuleAsync(id string) (Task, error) { 229 if id == "" { 230 return Task{}, fmt.Errorf("provided id is empty") 231 } 232 233 err := egw.Refresh() 234 if err != nil { 235 return Task{}, fmt.Errorf("error refreshing edge gateway: %s", err) 236 } 237 238 natServiceToUpdate := egw.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService 239 ruleIndex := -1 240 if natServiceToUpdate != nil { 241 for n, existingNatRule := range natServiceToUpdate.NatRule { 242 if existingNatRule.ID == id { 243 ruleIndex = n 244 break 245 } 246 } 247 } else { 248 return Task{}, fmt.Errorf("edge gateway doesn't have NAT rules") 249 } 250 251 if ruleIndex == -1 { 252 return Task{}, fmt.Errorf("edge gateway doesn't have rule with such ID") 253 } 254 255 if len(natServiceToUpdate.NatRule) > 1 { 256 natServiceToUpdate.NatRule = append(natServiceToUpdate.NatRule[:ruleIndex], natServiceToUpdate.NatRule[ruleIndex+1:]...) 257 } else { 258 natServiceToUpdate.NatRule = nil 259 } 260 261 newRules := &types.EdgeGatewayServiceConfiguration{ 262 Xmlns: types.XMLNamespaceVCloud, 263 NatService: natServiceToUpdate, 264 } 265 266 egwConfigureHref := urlParseRequestURI(egw.EdgeGateway.HREF) 267 egwConfigureHref.Path += "/action/configureServices" 268 269 // Return the task 270 return egw.client.ExecuteTaskRequest(egwConfigureHref.String(), http.MethodPost, 271 "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) 272 } 273 274 // AddDNATRule creates DNAT rule and returns the NAT struct that was created or an error. 275 // Allows assigning a specific Org VDC or an external network. 276 // When edge gateway is advanced vCD API uses element <tag> to map with NSX edge gateway ID. A known issue is 277 // that updating rule using User interface resets <tag> and as result mapping is lost. 278 // Getting using NatRule.ID won't be valid anymore. 279 // Old functions AddNATPortMapping and AddNATMapping assigned rule only to first external network 280 func (egw *EdgeGateway) AddDNATRule(ruleDetails NatRule) (*types.NatRule, error) { 281 mappingId, err := getPseudoUuid() 282 if err != nil { 283 return nil, err 284 } 285 originalDescription := ruleDetails.Description 286 ruleDetails.Description = mappingId 287 288 ruleDetails.NatType = "DNAT" 289 task, err := egw.AddNATRuleAsync(ruleDetails) 290 if err != nil { 291 return nil, fmt.Errorf("error creating DNAT rule: %s", err) 292 } 293 err = task.WaitTaskCompletion() 294 if err != nil { 295 return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) 296 } 297 298 var createdNatRule *types.NatRule 299 300 err = egw.Refresh() 301 if err != nil { 302 return nil, fmt.Errorf("error refreshing edge gateway: %s", err) 303 } 304 305 for _, natRule := range egw.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule { 306 if natRule.Description == mappingId { 307 createdNatRule = natRule 308 break 309 } 310 } 311 312 if createdNatRule == nil { 313 return nil, fmt.Errorf("error creating DNAT rule, didn't match created rule") 314 } 315 316 createdNatRule.Description = originalDescription 317 318 return egw.UpdateNatRule(createdNatRule) 319 } 320 321 // AddSNATRule creates SNAT rule and returns created NAT rule or error. 322 // Allows assigning a specific Org VDC or an external network. 323 // Old functions AddNATPortMapping and AddNATMapping aren't correct as assigned rule only to first external network 324 func (egw *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, description string) (*types.NatRule, error) { 325 326 // As vCD API doesn't return rule ID we get it manually: 327 // * create rule with description which value is our generated ID 328 // * find rule which has description with our generated ID 329 // * get the real (vCD's) rule ID 330 // * update description with real value and return nat rule 331 332 mappingId, err := getPseudoUuid() 333 if err != nil { 334 return nil, err 335 } 336 337 task, err := egw.AddNATRuleAsync(NatRule{NetworkHref: networkHref, NatType: "SNAT", ExternalIP: externalIP, 338 ExternalPort: "any", InternalIP: internalIP, InternalPort: "any", 339 IcmpSubType: "", Protocol: "any", Description: mappingId}) 340 if err != nil { 341 return nil, fmt.Errorf("error creating SNAT rule: %s", err) 342 } 343 err = task.WaitTaskCompletion() 344 if err != nil { 345 return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) 346 } 347 348 var createdNatRule *types.NatRule 349 350 err = egw.Refresh() 351 if err != nil { 352 return nil, fmt.Errorf("error refreshing edge gateway: %s", err) 353 } 354 355 for _, natRule := range egw.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule { 356 if natRule.Description == mappingId { 357 createdNatRule = natRule 358 break 359 } 360 } 361 362 if createdNatRule == nil { 363 return nil, fmt.Errorf("error creating SNAT rule, didn't match created rule") 364 } 365 366 createdNatRule.Description = description 367 368 return egw.UpdateNatRule(createdNatRule) 369 } 370 371 // getPseudoUuid creates unique ID/UUID 372 func getPseudoUuid() (string, error) { 373 374 b := make([]byte, 16) 375 _, err := rand.Read(b) 376 if err != nil { 377 return "", err 378 } 379 380 uuid := fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) 381 382 return uuid, nil 383 } 384 385 // UpdateNatRule updates NAT rule and handles task. Returns updated NAT rule or error. 386 func (egw *EdgeGateway) UpdateNatRule(natRule *types.NatRule) (*types.NatRule, error) { 387 task, err := egw.UpdateNatRuleAsync(natRule) 388 if err != nil { 389 return nil, fmt.Errorf("error updating NAT rule: %s", err) 390 } 391 err = task.WaitTaskCompletion() 392 if err != nil { 393 return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) 394 } 395 396 return egw.GetNatRule(natRule.ID) 397 } 398 399 // UpdateNatRuleAsync updates NAT rule and returns task or error. 400 func (egw *EdgeGateway) UpdateNatRuleAsync(natRule *types.NatRule) (Task, error) { 401 if natRule.GatewayNatRule.Protocol != "" && !isValidProtocol(natRule.GatewayNatRule.Protocol) { 402 return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") 403 } 404 405 if strings.ToUpper(natRule.GatewayNatRule.Protocol) == "ICMP" && !isValidIcmpSubType(natRule.GatewayNatRule.IcmpSubType) { 406 return Task{}, fmt.Errorf("provided icmp sub type is not correct") 407 } 408 409 err := egw.Refresh() 410 if err != nil { 411 return Task{}, fmt.Errorf("error refreshing edge gateway: %s", err) 412 } 413 414 natServiceToUpdate := egw.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService 415 416 if natServiceToUpdate != nil { 417 for n, existingNatRule := range natServiceToUpdate.NatRule { 418 if existingNatRule.ID == natRule.ID { 419 natServiceToUpdate.NatRule[n] = natRule 420 } 421 } 422 } else { 423 return Task{}, fmt.Errorf("edge gateway doesn't have such nat rule") 424 } 425 426 newRules := &types.EdgeGatewayServiceConfiguration{ 427 Xmlns: types.XMLNamespaceVCloud, 428 NatService: natServiceToUpdate, 429 } 430 431 egwConfigureHref := urlParseRequestURI(egw.EdgeGateway.HREF) 432 egwConfigureHref.Path += "/action/configureServices" 433 434 // Return the task 435 return egw.client.ExecuteTaskRequest(egwConfigureHref.String(), http.MethodPost, 436 "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) 437 } 438 439 // GetNatRule returns NAT rule or error. 440 func (egw *EdgeGateway) GetNatRule(id string) (*types.NatRule, error) { 441 err := egw.Refresh() 442 if err != nil { 443 return nil, fmt.Errorf("error refreshing edge gateway: %s", err) 444 } 445 446 if egw.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService != nil { 447 for _, natRule := range egw.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule { 448 if natRule.ID == id { 449 return natRule, nil 450 } 451 } 452 } 453 454 return nil, ErrorEntityNotFound 455 } 456 457 // AddNATRuleAsync creates NAT rule and return task or err 458 // Allows assigning specific network Org VDC or external. Old function AddNATPortMapping and 459 // AddNATMapping function shouldn't be used because assigns rule to first external network 460 func (egw *EdgeGateway) AddNATRuleAsync(ruleDetails NatRule) (Task, error) { 461 if !isValidProtocol(ruleDetails.Protocol) { 462 return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") 463 } 464 465 if strings.ToUpper(ruleDetails.Protocol) == "ICMP" && !isValidIcmpSubType(ruleDetails.IcmpSubType) { 466 return Task{}, fmt.Errorf("provided icmp sub type is not correct") 467 } 468 469 currentEdgeConfig := egw.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration 470 471 // Take care of the NAT service 472 newNatService := &types.NatService{} 473 474 if currentEdgeConfig.NatService == nil { 475 newNatService.IsEnabled = true 476 } else { 477 newNatService.IsEnabled = currentEdgeConfig.NatService.IsEnabled 478 newNatService.NatType = currentEdgeConfig.NatService.NatType 479 newNatService.Policy = currentEdgeConfig.NatService.Policy 480 newNatService.ExternalIP = currentEdgeConfig.NatService.ExternalIP 481 newNatService.NatRule = currentEdgeConfig.NatService.NatRule 482 } 483 484 //construct new rule 485 natRule := &types.NatRule{ 486 RuleType: ruleDetails.NatType, 487 IsEnabled: addrOf(true), 488 Description: ruleDetails.Description, 489 GatewayNatRule: &types.GatewayNatRule{ 490 Interface: &types.Reference{ 491 HREF: ruleDetails.NetworkHref, 492 }, 493 OriginalIP: ruleDetails.ExternalIP, 494 OriginalPort: ruleDetails.ExternalPort, 495 TranslatedIP: ruleDetails.InternalIP, 496 TranslatedPort: ruleDetails.InternalPort, 497 Protocol: ruleDetails.Protocol, 498 IcmpSubType: ruleDetails.IcmpSubType, 499 }, 500 } 501 502 newNatService.NatRule = append(newNatService.NatRule, natRule) 503 currentEdgeConfig.NatService = newNatService 504 newRules := &types.EdgeGatewayServiceConfiguration{ 505 Xmlns: types.XMLNamespaceVCloud, 506 NatService: newNatService, 507 } 508 509 egwConfigureHref := urlParseRequestURI(egw.EdgeGateway.HREF) 510 egwConfigureHref.Path += "/action/configureServices" 511 512 // Return the task 513 return egw.client.ExecuteTaskRequest(egwConfigureHref.String(), http.MethodPost, 514 "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) 515 } 516 517 // Deprecated: Use eGW.AddSNATRule() or eGW.AddDNATRule() 518 func (egw *EdgeGateway) AddNATRule(network *types.OrgVDCNetwork, natType, externalIP, internalIP string) (Task, error) { 519 return egw.AddNATPortMappingWithUplink(network, natType, externalIP, "any", internalIP, "any", "any", "") 520 } 521 522 // Deprecated: Use eGW.AddNATRule() 523 func (egw *EdgeGateway) AddNATMapping(natType, externalIP, internalIP string) (Task, error) { 524 return egw.AddNATPortMapping(natType, externalIP, "any", internalIP, "any", "any", "") 525 } 526 527 // Deprecated: Use eGW.AddNATPortMappingWithUplink() 528 func (egw *EdgeGateway) AddNATPortMapping(natType, externalIP, externalPort, internalIP, internalPort, protocol, icmpSubType string) (Task, error) { 529 return egw.AddNATPortMappingWithUplink(nil, natType, externalIP, externalPort, internalIP, internalPort, protocol, icmpSubType) 530 } 531 532 // Deprecated: creates not good behaviour of functionality 533 func (egw *EdgeGateway) getFirstUplink() types.Reference { 534 var uplink types.Reference 535 for _, gi := range egw.EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface { 536 if gi.InterfaceType != "uplink" { 537 continue 538 } 539 uplink = *gi.Network 540 } 541 return uplink 542 } 543 544 // Values are matched with VCD UI when creating DNAT for edge gateway. 545 func isValidProtocol(protocol string) bool { 546 switch strings.ToUpper(protocol) { 547 case 548 "TCP", 549 "UDP", 550 "TCPUDP", 551 "ICMP", 552 "ANY": 553 return true 554 } 555 return false 556 } 557 558 // Used values are named here https://code.vmware.com/apis/287/vcloud#/doc/doc/types/GatewayNatRuleType.html 559 // Also can be matched in VCD UI when creating DNAT for edge gateway. 560 func isValidIcmpSubType(protocol string) bool { 561 switch strings.ToLower(protocol) { 562 case 563 "address-mask-request", 564 "address-mask-reply", 565 "destination-unreachable", 566 "echo-request", 567 "echo-reply", 568 "parameter-problem", 569 "redirect", 570 "router-advertisement", 571 "router-solicitation", 572 "source-quench", 573 "time-exceeded", 574 "timestamp-request", 575 "timestamp-reply", 576 "any": 577 return true 578 } 579 return false 580 } 581 582 // Deprecated: Use eGW.AddDNATRule() or eGW.CreateNsxvNatRule() for NSX-V 583 func (egw *EdgeGateway) AddNATPortMappingWithUplink(network *types.OrgVDCNetwork, natType, externalIP, externalPort, internalIP, internalPort, protocol, icmpSubType string) (Task, error) { 584 // if a network is provided take it, otherwise find first uplink on the edge gateway 585 var uplinkRef string 586 587 if network != nil { 588 uplinkRef = network.HREF 589 } else { 590 // TODO: remove when method used this removed 591 uplinkRef = egw.getFirstUplink().HREF 592 } 593 594 if !isValidProtocol(protocol) { 595 return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") 596 } 597 598 if strings.ToUpper(protocol) == "ICMP" && !isValidIcmpSubType(icmpSubType) { 599 return Task{}, fmt.Errorf("provided icmp sub type is not correct") 600 } 601 602 newEdgeConfig := egw.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration 603 604 // Take care of the NAT service 605 newNatService := &types.NatService{} 606 607 if newEdgeConfig.NatService == nil { 608 newNatService.IsEnabled = true 609 } else { 610 newNatService.IsEnabled = newEdgeConfig.NatService.IsEnabled 611 newNatService.NatType = newEdgeConfig.NatService.NatType 612 newNatService.Policy = newEdgeConfig.NatService.Policy 613 newNatService.ExternalIP = newEdgeConfig.NatService.ExternalIP 614 615 for _, natRule := range newEdgeConfig.NatService.NatRule { 616 617 // Kludgy IF to avoid deleting DNAT rules not created by us. 618 // If matches, let's skip it and continue the loop 619 if natRule.RuleType == natType && 620 natRule.GatewayNatRule.OriginalIP == externalIP && 621 natRule.GatewayNatRule.OriginalPort == externalPort && 622 natRule.GatewayNatRule.TranslatedIP == internalIP && 623 natRule.GatewayNatRule.TranslatedPort == internalPort && 624 natRule.GatewayNatRule.Interface.HREF == uplinkRef { 625 continue 626 } 627 628 newNatService.NatRule = append(newNatService.NatRule, natRule) 629 } 630 } 631 632 //add rule 633 natRule := &types.NatRule{ 634 RuleType: natType, 635 IsEnabled: addrOf(true), 636 GatewayNatRule: &types.GatewayNatRule{ 637 Interface: &types.Reference{ 638 HREF: uplinkRef, 639 }, 640 OriginalIP: externalIP, 641 OriginalPort: externalPort, 642 TranslatedIP: internalIP, 643 TranslatedPort: internalPort, 644 Protocol: protocol, 645 IcmpSubType: icmpSubType, 646 }, 647 } 648 newNatService.NatRule = append(newNatService.NatRule, natRule) 649 650 newEdgeConfig.NatService = newNatService 651 652 newRules := &types.EdgeGatewayServiceConfiguration{ 653 Xmlns: types.XMLNamespaceVCloud, 654 NatService: newNatService, 655 } 656 657 apiEndpoint := urlParseRequestURI(egw.EdgeGateway.HREF) 658 apiEndpoint.Path += "/action/configureServices" 659 660 // Return the task 661 return egw.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, 662 "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) 663 } 664 665 func (egw *EdgeGateway) CreateFirewallRules(defaultAction string, rules []*types.FirewallRule) (Task, error) { 666 err := egw.Refresh() 667 if err != nil { 668 return Task{}, fmt.Errorf("error: %s", err) 669 } 670 671 newRules := &types.EdgeGatewayServiceConfiguration{ 672 Xmlns: types.XMLNamespaceVCloud, 673 FirewallService: &types.FirewallService{ 674 IsEnabled: true, 675 DefaultAction: defaultAction, 676 LogDefaultAction: true, 677 FirewallRule: rules, 678 }, 679 } 680 681 output, err := xml.MarshalIndent(newRules, " ", " ") 682 if err != nil { 683 return Task{}, fmt.Errorf("error: %s", err) 684 } 685 686 var resp *http.Response 687 for { 688 buffer := bytes.NewBufferString(xml.Header + string(output)) 689 690 apiEndpoint := urlParseRequestURI(egw.EdgeGateway.HREF) 691 apiEndpoint.Path += "/action/configureServices" 692 693 req := egw.client.NewRequest(map[string]string{}, http.MethodPost, *apiEndpoint, buffer) 694 util.Logger.Printf("[DEBUG] POSTING TO URL: %s", apiEndpoint.Path) 695 util.Logger.Printf("[DEBUG] XML TO SEND:\n%s", buffer) 696 697 req.Header.Add("Content-Type", "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml") 698 699 resp, err = checkResp(egw.client.Http.Do(req)) 700 if err != nil { 701 if reErrorBusy.MatchString(err.Error()) { 702 time.Sleep(3 * time.Second) 703 continue 704 } 705 return Task{}, fmt.Errorf("error reconfiguring Edge Gateway: %s", err) 706 } 707 break 708 } 709 710 task := NewTask(egw.client) 711 712 if err = decodeBody(types.BodyTypeXML, resp, task.Task); err != nil { 713 return Task{}, fmt.Errorf("error decoding Task response: %s", err) 714 } 715 716 // The request was successful 717 return *task, nil 718 } 719 720 func (egw *EdgeGateway) Refresh() error { 721 722 if egw.EdgeGateway == nil { 723 return fmt.Errorf("cannot refresh, Object is empty") 724 } 725 726 url := egw.EdgeGateway.HREF 727 728 // Empty struct before a new unmarshal, otherwise we end up with duplicate 729 // elements in slices. 730 egw.EdgeGateway = &types.EdgeGateway{} 731 732 _, err := egw.client.ExecuteRequest(url, http.MethodGet, 733 "", "error retrieving Edge Gateway: %s", nil, egw.EdgeGateway) 734 735 return err 736 } 737 738 func (egw *EdgeGateway) Remove1to1Mapping(internal, external string) (Task, error) { 739 740 // Refresh EdgeGateway rules 741 err := egw.Refresh() 742 if err != nil { 743 fmt.Printf("error: %s\n", err) 744 } 745 746 var uplinkif string 747 for _, gifs := range egw.EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface { 748 if gifs.InterfaceType == "uplink" { 749 uplinkif = gifs.Network.HREF 750 } 751 } 752 753 newEdgeConfig := egw.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration 754 755 // Take care of the NAT service 756 newNatService := &types.NatService{} 757 758 // Copy over the NAT configuration 759 newNatService.IsEnabled = newEdgeConfig.NatService.IsEnabled 760 newNatService.NatType = newEdgeConfig.NatService.NatType 761 newNatService.Policy = newEdgeConfig.NatService.Policy 762 newNatService.ExternalIP = newEdgeConfig.NatService.ExternalIP 763 764 for i, natRule := range newEdgeConfig.NatService.NatRule { 765 766 // Kludgy IF to avoid deleting DNAT rules not created by us. 767 // If matches, let's skip it and continue the loop 768 if natRule.RuleType == "DNAT" && 769 natRule.GatewayNatRule.OriginalIP == external && 770 natRule.GatewayNatRule.TranslatedIP == internal && 771 natRule.GatewayNatRule.OriginalPort == "any" && 772 natRule.GatewayNatRule.TranslatedPort == "any" && 773 natRule.GatewayNatRule.Protocol == "any" && 774 natRule.GatewayNatRule.Interface.HREF == uplinkif { 775 continue 776 } 777 778 // Kludgy IF to avoid deleting SNAT rules not created by us. 779 // If matches, let's skip it and continue the loop 780 if natRule.RuleType == "SNAT" && 781 natRule.GatewayNatRule.OriginalIP == internal && 782 natRule.GatewayNatRule.TranslatedIP == external && 783 natRule.GatewayNatRule.Interface.HREF == uplinkif { 784 continue 785 } 786 787 // If doesn't match the above IFs, it's something we need to preserve, 788 // let's add it to the new NatService struct 789 newNatService.NatRule = append(newNatService.NatRule, newEdgeConfig.NatService.NatRule[i]) 790 791 } 792 793 // Fill the new NatService Section 794 newEdgeConfig.NatService = newNatService 795 796 // Take care of the Firewall service 797 newFwService := &types.FirewallService{} 798 799 // Copy over the firewall configuration 800 newFwService.IsEnabled = newEdgeConfig.FirewallService.IsEnabled 801 newFwService.DefaultAction = newEdgeConfig.FirewallService.DefaultAction 802 newFwService.LogDefaultAction = newEdgeConfig.FirewallService.LogDefaultAction 803 804 for i, firewallRule := range newEdgeConfig.FirewallService.FirewallRule { 805 806 // Kludgy IF to avoid deleting inbound FW rules not created by us. 807 // If matches, let's skip it and continue the loop 808 if firewallRule.Policy == "allow" && 809 firewallRule.Protocols.Any && 810 firewallRule.DestinationPortRange == "Any" && 811 firewallRule.SourcePortRange == "Any" && 812 firewallRule.SourceIP == "Any" && 813 firewallRule.DestinationIP == external { 814 continue 815 } 816 817 // Kludgy IF to avoid deleting outbound FW rules not created by us. 818 // If matches, let's skip it and continue the loop 819 if firewallRule.Policy == "allow" && 820 firewallRule.Protocols.Any && 821 firewallRule.DestinationPortRange == "Any" && 822 firewallRule.SourcePortRange == "Any" && 823 firewallRule.SourceIP == internal && 824 firewallRule.DestinationIP == "Any" { 825 continue 826 } 827 828 // If doesn't match the above IFs, it's something we need to preserve, 829 // let's add it to the new FirewallService struct 830 newFwService.FirewallRule = append(newFwService.FirewallRule, newEdgeConfig.FirewallService.FirewallRule[i]) 831 832 } 833 834 // Fill the new FirewallService Section 835 newEdgeConfig.FirewallService = newFwService 836 837 // Fix 838 newEdgeConfig.NatService.IsEnabled = true 839 840 apiEndpoint := urlParseRequestURI(egw.EdgeGateway.HREF) 841 apiEndpoint.Path += "/action/configureServices" 842 843 // Return the task 844 return egw.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, 845 "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newEdgeConfig) 846 847 } 848 849 func (egw *EdgeGateway) Create1to1Mapping(internal, external, description string) (Task, error) { 850 851 // Refresh EdgeGateway rules 852 err := egw.Refresh() 853 if err != nil { 854 fmt.Printf("error: %s\n", err) 855 } 856 857 var uplinkif string 858 for _, gifs := range egw.EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface { 859 if gifs.InterfaceType == "uplink" { 860 uplinkif = gifs.Network.HREF 861 } 862 } 863 864 newEdgeConfig := egw.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration 865 866 snat := &types.NatRule{ 867 Description: description, 868 RuleType: "SNAT", 869 IsEnabled: addrOf(true), 870 GatewayNatRule: &types.GatewayNatRule{ 871 Interface: &types.Reference{ 872 HREF: uplinkif, 873 }, 874 OriginalIP: internal, 875 TranslatedIP: external, 876 Protocol: "any", 877 }, 878 } 879 880 if newEdgeConfig.NatService == nil { 881 newEdgeConfig.NatService = &types.NatService{} 882 } 883 newEdgeConfig.NatService.NatRule = append(newEdgeConfig.NatService.NatRule, snat) 884 885 dnat := &types.NatRule{ 886 Description: description, 887 RuleType: "DNAT", 888 IsEnabled: addrOf(true), 889 GatewayNatRule: &types.GatewayNatRule{ 890 Interface: &types.Reference{ 891 HREF: uplinkif, 892 }, 893 OriginalIP: external, 894 OriginalPort: "any", 895 TranslatedIP: internal, 896 TranslatedPort: "any", 897 Protocol: "any", 898 }, 899 } 900 901 newEdgeConfig.NatService.NatRule = append(newEdgeConfig.NatService.NatRule, dnat) 902 903 fwin := &types.FirewallRule{ 904 Description: description, 905 IsEnabled: true, 906 Policy: "allow", 907 Protocols: &types.FirewallRuleProtocols{ 908 Any: true, 909 }, 910 DestinationPortRange: "Any", 911 DestinationIP: external, 912 SourcePortRange: "Any", 913 SourceIP: "Any", 914 EnableLogging: false, 915 } 916 917 newEdgeConfig.FirewallService.FirewallRule = append(newEdgeConfig.FirewallService.FirewallRule, fwin) 918 919 fwout := &types.FirewallRule{ 920 Description: description, 921 IsEnabled: true, 922 Policy: "allow", 923 Protocols: &types.FirewallRuleProtocols{ 924 Any: true, 925 }, 926 DestinationPortRange: "Any", 927 DestinationIP: "Any", 928 SourcePortRange: "Any", 929 SourceIP: internal, 930 EnableLogging: false, 931 } 932 933 newEdgeConfig.FirewallService.FirewallRule = append(newEdgeConfig.FirewallService.FirewallRule, fwout) 934 935 apiEndpoint := urlParseRequestURI(egw.EdgeGateway.HREF) 936 apiEndpoint.Path += "/action/configureServices" 937 938 // Return the task 939 return egw.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, 940 "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newEdgeConfig) 941 942 } 943 944 func (egw *EdgeGateway) AddIpsecVPN(ipsecVPNConfig *types.EdgeGatewayServiceConfiguration) (Task, error) { 945 946 err := egw.Refresh() 947 if err != nil { 948 fmt.Printf("error: %s\n", err) 949 } 950 951 ipsecVPNConfig.Xmlns = types.XMLNamespaceVCloud 952 953 apiEndpoint := urlParseRequestURI(egw.EdgeGateway.HREF) 954 apiEndpoint.Path += "/action/configureServices" 955 956 // Return the task 957 return egw.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, 958 "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", ipsecVPNConfig) 959 960 } 961 962 // Removes an Edge Gateway VPN, by passing an empty configuration 963 func (egw *EdgeGateway) RemoveIpsecVPN() (Task, error) { 964 err := egw.Refresh() 965 if err != nil { 966 fmt.Printf("error: %s\n", err) 967 } 968 ipsecVPNConfig := &types.EdgeGatewayServiceConfiguration{ 969 Xmlns: types.XMLNamespaceVCloud, 970 GatewayIpsecVpnService: &types.GatewayIpsecVpnService{ 971 IsEnabled: false, 972 }, 973 } 974 return egw.AddIpsecVPN(ipsecVPNConfig) 975 } 976 977 // Deletes the edge gateway, returning a task and an error with the operation result. 978 // https://code.vmware.com/apis/442/vcloud-director/doc/doc/operations/DELETE-EdgeGateway.html 979 func (egw *EdgeGateway) DeleteAsync(force bool, recursive bool) (Task, error) { 980 util.Logger.Printf("[TRACE] EdgeGateway.Delete - deleting edge gateway with force: %t, recursive: %t", force, recursive) 981 982 if egw.EdgeGateway.HREF == "" { 983 return Task{}, fmt.Errorf("cannot delete, HREF is missing") 984 } 985 986 egwUrl, err := url.ParseRequestURI(egw.EdgeGateway.HREF) 987 if err != nil { 988 return Task{}, fmt.Errorf("error parsing edge gateway url: %s", err) 989 } 990 991 req := egw.client.NewRequest(map[string]string{ 992 "force": strconv.FormatBool(force), 993 "recursive": strconv.FormatBool(recursive), 994 }, http.MethodDelete, *egwUrl, nil) 995 resp, err := checkResp(egw.client.Http.Do(req)) 996 if err != nil { 997 return Task{}, fmt.Errorf("error deleting edge gateway: %s", err) 998 } 999 task := NewTask(egw.client) 1000 if err = decodeBody(types.BodyTypeXML, resp, task.Task); err != nil { 1001 return Task{}, fmt.Errorf("error decoding task response: %s", err) 1002 } 1003 return *task, err 1004 } 1005 1006 // Deletes the edge gateway, returning an error with the operation result. 1007 // https://code.vmware.com/apis/442/vcloud-director/doc/doc/operations/DELETE-EdgeGateway.html 1008 func (egw *EdgeGateway) Delete(force bool, recursive bool) error { 1009 1010 task, err := egw.DeleteAsync(force, recursive) 1011 if err != nil { 1012 return err 1013 } 1014 if task.Task.Status == "error" { 1015 return fmt.Errorf(combinedTaskErrorMessage(task.Task, fmt.Errorf("edge gateway not properly destroyed"))) 1016 } 1017 1018 err = task.WaitTaskCompletion() 1019 if err != nil { 1020 return fmt.Errorf(combinedTaskErrorMessage(task.Task, err)) 1021 } 1022 1023 return nil 1024 } 1025 1026 // GetNetworks returns the list of networks associated with an edge gateway 1027 // In the return structure, an interfaceType of "uplink" indicates an external network, 1028 // while "internal" is for Org VDC routed networks 1029 func (egw *EdgeGateway) GetNetworks() ([]SimpleNetworkIdentifier, error) { 1030 var networks []SimpleNetworkIdentifier 1031 err := egw.Refresh() 1032 if err != nil { 1033 return networks, err 1034 } 1035 for _, net := range egw.EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface { 1036 netIdentifier := SimpleNetworkIdentifier{ 1037 Name: net.Name, 1038 InterfaceType: net.InterfaceType, 1039 } 1040 networks = append(networks, netIdentifier) 1041 } 1042 1043 return networks, nil 1044 } 1045 1046 // HasDefaultGateway returns true if the edge gateway uses one of the external 1047 // networks as default gateway 1048 func (egw *EdgeGateway) HasDefaultGateway() bool { 1049 if egw.EdgeGateway.Configuration != nil && 1050 egw.EdgeGateway.Configuration.GatewayInterfaces != nil { 1051 for _, gw := range egw.EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface { 1052 // Check if the interface is used for default route 1053 if gw.UseForDefaultRoute { 1054 // Look for a specific subnet which is used as a default route 1055 for _, subnetParticipation := range gw.SubnetParticipation { 1056 if subnetParticipation.UseForDefaultRoute && 1057 subnetParticipation.Gateway != "" && 1058 subnetParticipation.Netmask != "" { 1059 return true 1060 } 1061 } 1062 } 1063 } 1064 } 1065 return false 1066 } 1067 1068 // HasAdvancedNetworking returns true if the edge gateway has advanced network configuration enabled 1069 func (egw *EdgeGateway) HasAdvancedNetworking() bool { 1070 return egw.EdgeGateway.Configuration != nil && 1071 egw.EdgeGateway.Configuration.AdvancedNetworkingEnabled != nil && 1072 *egw.EdgeGateway.Configuration.AdvancedNetworkingEnabled 1073 } 1074 1075 // buildProxiedEdgeEndpointURL helps to get root endpoint for Edge Gateway using the 1076 // NSX API Proxy and can append optionalSuffix which must have its own leading / 1077 func (egw *EdgeGateway) buildProxiedEdgeEndpointURL(optionalSuffix string) (string, error) { 1078 apiEndpoint, err := url.ParseRequestURI(egw.EdgeGateway.HREF) 1079 if err != nil { 1080 return "", fmt.Errorf("unable to process edge gateway URL: %s", err) 1081 } 1082 edgeID := strings.Split(egw.EdgeGateway.ID, ":") 1083 if len(edgeID) != 4 { 1084 return "", fmt.Errorf("unable to find edge gateway id: %s", egw.EdgeGateway.ID) 1085 } 1086 hostname := apiEndpoint.Scheme + "://" + apiEndpoint.Host + "/network/edges/" + edgeID[3] 1087 1088 if optionalSuffix != "" { 1089 return hostname + optionalSuffix, nil 1090 } 1091 1092 return hostname, nil 1093 } 1094 1095 // GetLBGeneralParams retrieves load balancer configuration of `&types.LoadBalancer` and can be used 1096 // to access global configuration options. These are 4 fields only: 1097 // LoadBalancer.Enabled, LoadBalancer.AccelerationEnabled, LoadBalancer.Logging.Enable, 1098 // LoadBalancer.Logging.LogLevel 1099 func (egw *EdgeGateway) GetLBGeneralParams() (*types.LbGeneralParamsWithXml, error) { 1100 if !egw.HasAdvancedNetworking() { 1101 return nil, fmt.Errorf("only advanced edge gateway supports load balancing") 1102 } 1103 1104 httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbConfigPath) 1105 if err != nil { 1106 return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) 1107 } 1108 1109 loadBalancerConfig := &types.LbGeneralParamsWithXml{} 1110 _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, 1111 "unable to read load balancer configuration: %s", nil, loadBalancerConfig) 1112 1113 if err != nil { 1114 return nil, err 1115 } 1116 1117 return loadBalancerConfig, nil 1118 } 1119 1120 // UpdateLBGeneralParams allows to update global load balancer configuration. 1121 // It accepts four fields (Enabled, AccelerationEnabled, Logging.Enable, Logging.LogLevel) and uses 1122 // them to construct types.LbGeneralParamsWithXml without altering other options to prevent config 1123 // corruption. 1124 // They are represented in load balancer global configuration tab in the UI. 1125 func (egw *EdgeGateway) UpdateLBGeneralParams(enabled, accelerationEnabled, loggingEnabled bool, logLevel string) (*types.LbGeneralParamsWithXml, error) { 1126 if !egw.HasAdvancedNetworking() { 1127 return nil, fmt.Errorf("only advanced edge gateway supports load balancing") 1128 } 1129 1130 if err := validateUpdateLBGeneralParams(logLevel); err != nil { 1131 return nil, err 1132 } 1133 // Retrieve load balancer to work on latest configuration 1134 currentLb, err := egw.GetLBGeneralParams() 1135 if err != nil { 1136 return nil, fmt.Errorf("unable to retrieve load balancer before update: %s", err) 1137 } 1138 1139 // Check if change is needed. If not - return early. 1140 if currentLb.Logging != nil && 1141 currentLb.Enabled == enabled && currentLb.AccelerationEnabled == accelerationEnabled && 1142 currentLb.Logging.Enable == loggingEnabled && currentLb.Logging.LogLevel == logLevel { 1143 return currentLb, nil 1144 } 1145 1146 // Modify only the global configuration settings 1147 currentLb.Enabled = enabled 1148 currentLb.AccelerationEnabled = accelerationEnabled 1149 currentLb.Logging = &types.LbLogging{ 1150 Enable: loggingEnabled, 1151 LogLevel: logLevel, 1152 } 1153 // Omit the version as it is updated automatically with each put 1154 currentLb.Version = "" 1155 1156 // Push updated configuration 1157 httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbConfigPath) 1158 if err != nil { 1159 return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) 1160 } 1161 _, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime, 1162 "error while updating load balancer config: %s", currentLb, &types.NSXError{}) 1163 if err != nil { 1164 return nil, err 1165 } 1166 1167 // Retrieve configuration after update 1168 updatedLb, err := egw.GetLBGeneralParams() 1169 if err != nil { 1170 return nil, fmt.Errorf("unable to retrieve load balancer config after update: %s", err) 1171 } 1172 1173 return updatedLb, nil 1174 } 1175 1176 // GetFirewallConfig retrieves firewall configuration and can be used 1177 // to alter main configuration options. These are 3 fields only: 1178 // FirewallConfigWithXml.Enabled, FirewallConfigWithXml.DefaultPolicy.LoggingEnabled and 1179 // FirewallConfigWithXml.DefaultPolicy.Action 1180 func (egw *EdgeGateway) GetFirewallConfig() (*types.FirewallConfigWithXml, error) { 1181 if !egw.HasAdvancedNetworking() { 1182 return nil, fmt.Errorf("only advanced edge gateway support firewall configuration") 1183 } 1184 1185 httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeFirewallPath) 1186 if err != nil { 1187 return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) 1188 } 1189 1190 firewallConfig := &types.FirewallConfigWithXml{} 1191 _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, 1192 "unable to read firewall configuration: %s", nil, firewallConfig) 1193 1194 if err != nil { 1195 return nil, err 1196 } 1197 1198 return firewallConfig, nil 1199 } 1200 1201 // UpdateFirewallConfig allows to update firewall configuration. 1202 // It accepts three fields (Enabled, DefaultLoggingEnabled, DefaultAction) and uses 1203 // them to construct types.FirewallConfigWithXml without altering other options to prevent config 1204 // corruption. 1205 // They are represented in firewall configuration page in the UI. 1206 func (egw *EdgeGateway) UpdateFirewallConfig(enabled, defaultLoggingEnabled bool, defaultAction string) (*types.FirewallConfigWithXml, error) { 1207 if !egw.HasAdvancedNetworking() { 1208 return nil, fmt.Errorf("only advanced edge gateway supports load balancing") 1209 } 1210 1211 if defaultAction != "accept" && defaultAction != "deny" { 1212 return nil, fmt.Errorf("default action must be either 'accept' or 'deny'") 1213 } 1214 1215 // Retrieve firewall latest configuration 1216 currentFw, err := egw.GetFirewallConfig() 1217 if err != nil { 1218 return nil, fmt.Errorf("unable to retrieve firewall config before update: %s", err) 1219 } 1220 1221 // Check if change is needed. If not - return early. 1222 if currentFw.Enabled == enabled && currentFw.DefaultPolicy.LoggingEnabled == defaultLoggingEnabled && 1223 currentFw.DefaultPolicy.Action == defaultAction { 1224 return currentFw, nil 1225 } 1226 1227 // Modify only the global configuration settings 1228 currentFw.Enabled = enabled 1229 currentFw.DefaultPolicy.LoggingEnabled = defaultLoggingEnabled 1230 currentFw.DefaultPolicy.Action = defaultAction 1231 1232 // Omit the version as it is updated automatically with each put 1233 currentFw.Version = "" 1234 1235 // Push updated configuration 1236 httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeFirewallPath) 1237 if err != nil { 1238 return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) 1239 } 1240 _, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime, 1241 "error while updating firewall configuration : %s", currentFw, &types.NSXError{}) 1242 if err != nil { 1243 return nil, err 1244 } 1245 1246 // Retrieve configuration after update 1247 updatedFw, err := egw.GetFirewallConfig() 1248 if err != nil { 1249 return nil, fmt.Errorf("unable to retrieve firewall after update: %s", err) 1250 } 1251 1252 return updatedFw, nil 1253 } 1254 1255 // validateUpdateLoadBalancer validates mandatory fields for global load balancer configuration 1256 // settings 1257 func validateUpdateLBGeneralParams(logLevel string) error { 1258 if logLevel == "" { 1259 return fmt.Errorf("field Logging.LogLevel must be set to update load balancer") 1260 } 1261 1262 return nil 1263 } 1264 1265 // getVdcNetworks retrieves a structure of type EdgeGatewayInterfaces which contains network 1266 // interfaces available in Edge Gateway (uses "/vdcNetworks" endpoint) 1267 func (egw *EdgeGateway) getVdcNetworks() (*types.EdgeGatewayInterfaces, error) { 1268 if !egw.HasAdvancedNetworking() { 1269 return nil, fmt.Errorf("only advanced edge gateway supports vNics") 1270 } 1271 1272 httpPath, err := egw.buildProxiedEdgeEndpointURL("/vdcNetworks") 1273 if err != nil { 1274 return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) 1275 } 1276 1277 vnicConfig := &types.EdgeGatewayInterfaces{} 1278 _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, 1279 "unable to edge gateway vnic configuration: %s", nil, vnicConfig) 1280 1281 if err != nil { 1282 return nil, err 1283 } 1284 1285 return vnicConfig, nil 1286 } 1287 1288 // GetVnicIndexByNetworkNameAndType returns *int of vNic index for specified network name and network type 1289 // networkType one of: 'internal', 'uplink', 'trunk', 'subinterface' 1290 // networkName cannot be empty 1291 func (egw *EdgeGateway) GetVnicIndexByNetworkNameAndType(networkName, networkType string) (*int, error) { 1292 vnics, err := egw.getVdcNetworks() 1293 if err != nil { 1294 return nil, fmt.Errorf("cannot retrieve vNic configuration: %s", err) 1295 } 1296 return getVnicIndexByNetworkNameAndType(networkName, networkType, vnics) 1297 } 1298 1299 // GetAnyVnicIndexByNetworkName parses XML structure of vNic mapping to networks in edge gateway XML 1300 // and returns *int of vNic index and network type by network name 1301 // networkName cannot be empty 1302 // networkType will be one of: 'internal', 'uplink', 'trunk', 'subinterface' 1303 // 1304 // Warning: this function assumes that there are no duplicate network names attached. If it is so 1305 // this function will return the first network 1306 func (egw *EdgeGateway) GetAnyVnicIndexByNetworkName(networkName string) (*int, string, error) { 1307 vnics, err := egw.getVdcNetworks() 1308 if err != nil { 1309 return nil, "", fmt.Errorf("cannot retrieve vNic configuration: %s", err) 1310 } 1311 1312 var foundVnicIndex *int 1313 var foundVnicType string 1314 1315 possibleNicTypes := []string{types.EdgeGatewayVnicTypeUplink, types.EdgeGatewayVnicTypeInternal, 1316 types.EdgeGatewayVnicTypeTrunk, types.EdgeGatewayVnicTypeSubinterface} 1317 1318 for _, nicType := range possibleNicTypes { 1319 vNicIndex, err := getVnicIndexByNetworkNameAndType(networkName, nicType, vnics) 1320 if err == nil { // nil error means we have found nic 1321 foundVnicIndex = vNicIndex 1322 foundVnicType = nicType 1323 break 1324 } 1325 } 1326 1327 if foundVnicIndex == nil && foundVnicType == "" { 1328 return nil, "", ErrorEntityNotFound 1329 } 1330 return foundVnicIndex, foundVnicType, nil 1331 } 1332 1333 // GetNetworkNameAndTypeByVnicIndex returns network name and network type for given vNic index 1334 // returned networkType can be one of: 'internal', 'uplink', 'trunk', 'subinterface' 1335 func (egw *EdgeGateway) GetNetworkNameAndTypeByVnicIndex(vNicIndex int) (string, string, error) { 1336 vnics, err := egw.getVdcNetworks() 1337 if err != nil { 1338 return "", "", fmt.Errorf("cannot retrieve vNic configuration: %s", err) 1339 } 1340 return getNetworkNameAndTypeByVnicIndex(vNicIndex, vnics) 1341 } 1342 1343 // getVnicIndexByNetworkNameAndType is wrapped and used by public function GetVnicIndexByNetworkNameAndType 1344 func getVnicIndexByNetworkNameAndType(networkName, networkType string, vnics *types.EdgeGatewayInterfaces) (*int, error) { 1345 if networkName == "" { 1346 return nil, fmt.Errorf("network name cannot be empty") 1347 } 1348 if networkType != types.EdgeGatewayVnicTypeUplink && 1349 networkType != types.EdgeGatewayVnicTypeInternal && 1350 networkType != types.EdgeGatewayVnicTypeTrunk && 1351 networkType != types.EdgeGatewayVnicTypeSubinterface { 1352 return nil, fmt.Errorf("networkType must be one of 'uplink', 'internal', 'trunk', 'subinterface'") 1353 } 1354 1355 var foundIndex *int 1356 foundCount := 0 1357 1358 for _, vnic := range vnics.EdgeInterface { 1359 // Look for matching portgroup name and network type. If the PortgroupName is not empty - 1360 // check that it contains network name as well. 1361 if vnic.Name == networkName && vnic.Type == networkType && 1362 (vnic.PortgroupName == networkName || vnic.PortgroupName == "") { 1363 foundIndex = vnic.Index 1364 foundCount++ 1365 } 1366 } 1367 1368 if foundCount > 1 { 1369 return nil, fmt.Errorf("more than one (%d) networks of type '%s' with name '%s' found", 1370 foundCount, networkType, networkName) 1371 } 1372 1373 if foundCount == 0 { 1374 return nil, ErrorEntityNotFound 1375 } 1376 1377 return foundIndex, nil 1378 } 1379 1380 // getNetworkNameAndTypeByVnicIndex looks up network type and name in list of edge gateway interfaces 1381 func getNetworkNameAndTypeByVnicIndex(vNicIndex int, vnics *types.EdgeGatewayInterfaces) (string, string, error) { 1382 if vNicIndex < 0 { 1383 return "", "", fmt.Errorf("vNic index cannot be negative") 1384 } 1385 1386 foundCount := 0 1387 var networkName, networkType string 1388 1389 for _, vnic := range vnics.EdgeInterface { 1390 if vnic.Index != nil && *vnic.Index == vNicIndex { 1391 foundCount++ 1392 networkName = vnic.Name 1393 networkType = vnic.Type 1394 } 1395 } 1396 1397 if foundCount > 1 { 1398 return "", "", fmt.Errorf("more than one networks found for vNic %d", vNicIndex) 1399 } 1400 1401 if foundCount == 0 { 1402 return "", "", ErrorEntityNotFound 1403 } 1404 1405 return networkName, networkType, nil 1406 } 1407 1408 // UpdateAsync updates the edge gateway in place with the information contained in the internal structure 1409 func (egw *EdgeGateway) UpdateAsync() (Task, error) { 1410 1411 egw.EdgeGateway.Xmlns = types.XMLNamespaceVCloud 1412 egw.EdgeGateway.Configuration.Xmlns = types.XMLNamespaceVCloud 1413 egw.EdgeGateway.Tasks = nil 1414 1415 // Return the task 1416 return egw.client.ExecuteTaskRequest(egw.EdgeGateway.HREF, http.MethodPut, 1417 types.MimeEdgeGateway, "error updating Edge Gateway: %s", egw.EdgeGateway) 1418 } 1419 1420 // Update is a wrapper around UpdateAsync 1421 // The pointer receiver is refreshed after update 1422 func (egw *EdgeGateway) Update() error { 1423 1424 task, err := egw.UpdateAsync() 1425 if err != nil { 1426 return err 1427 } 1428 err = task.WaitTaskCompletion() 1429 if err != nil { 1430 return err 1431 } 1432 return egw.Refresh() 1433 }