Skip to content

Commit

Permalink
Add support for Edge transport nodes which were created externally
Browse files Browse the repository at this point in the history
Normally users create Edge Transport Nodes within NSX, which deploys them into the regsitered compute manager.
However, users have the option to do the same by deploying the Edge appliance anywhere, outside NSX, and registering it with NSX using the Edge CLI or OVA parameters.
Later, this Edge appliance can be converted into a transport node using NSX API - this change attempts to utilize this capability.

Fixes: #1459
Signed-off-by: Kobi Samoray <[email protected]>
  • Loading branch information
ksamoray authored and salv-orlando committed Dec 19, 2024
1 parent 1d7197d commit 204cd2b
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 40 deletions.
136 changes: 98 additions & 38 deletions nsxt/resource_nsxt_edge_transport_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ func resourceNsxtEdgeTransportNode() *schema.Resource {
"description": getDescriptionSchema(),
"display_name": getDisplayNameSchema(),
"tag": getTagsSchema(),
"node_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "Unique Id of the fabric node",
ConflictsWith: []string{"ip_addresses", "fqdn", "deployment_config", "external_id"},
},
"failure_domain": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -671,43 +678,56 @@ func getTransportNodeFromSchema(d *schema.ResourceData, m interface{}) (*mpmodel
description := d.Get("description").(string)
displayName := d.Get("display_name").(string)
tags := getMPTagsFromSchema(d)
nodeID := d.Get("node_id").(string)
failureDomain := d.Get("failure_domain").(string)
hostSwitchSpec, err := getHostSwitchSpecFromSchema(d, m, nodeTypeEdge)
if err != nil {
return nil, fmt.Errorf("failed to create Transport Node: %v", err)
}

converter := bindings.NewTypeConverter()
var dataValue data.DataValue
var errs []error
var nodeDeploymentInfo *data.StructValue
if nodeID == "" {
/*
node_id attribute conflicts with node_deployment_info. As node_deployment_info is a complex object which has
attributes with default values, this schema property will always have values - therefore there is no simple way
to enforce this conflict within the provider (e.g check if node_id and node_deployment_info have values, then
fail.
So the provider will ignore node_deployment_info properties when node_id has a value - which would mean that
this edge appliance was created externally.
*/
log.Printf("node_id not specified, will deploy edge using values in deploymentConfig")
converter := bindings.NewTypeConverter()
var dataValue data.DataValue
var errs []error

externalID := d.Get("external_id").(string)
fqdn := d.Get("fqdn").(string)
ipAddresses := interfaceListToStringList(d.Get("ip_addresses").([]interface{}))
externalID := d.Get("external_id").(string)
fqdn := d.Get("fqdn").(string)
ipAddresses := interfaceListToStringList(d.Get("ip_addresses").([]interface{}))

deploymentConfig, err := getEdgeNodeDeploymentConfigFromSchema(d.Get("deployment_config"))
if err != nil {
return nil, err
}
nodeSettings, err := getEdgeNodeSettingsFromSchema(d.Get("node_settings"))
if err != nil {
return nil, err
}
node := mpmodel.EdgeNode{
ExternalId: &externalID,
Fqdn: &fqdn,
IpAddresses: ipAddresses,
DeploymentConfig: deploymentConfig,
NodeSettings: nodeSettings,
ResourceType: mpmodel.EdgeNode__TYPE_IDENTIFIER,
}
dataValue, errs = converter.ConvertToVapi(node, mpmodel.EdgeNodeBindingType())
deploymentConfig, err := getEdgeNodeDeploymentConfigFromSchema(d.Get("deployment_config"))
if err != nil {
return nil, err
}
nodeSettings, err := getEdgeNodeSettingsFromSchema(d.Get("node_settings"))
if err != nil {
return nil, err
}
node := mpmodel.EdgeNode{
ExternalId: &externalID,
Fqdn: &fqdn,
IpAddresses: ipAddresses,
DeploymentConfig: deploymentConfig,
NodeSettings: nodeSettings,
ResourceType: mpmodel.EdgeNode__TYPE_IDENTIFIER,
}
dataValue, errs = converter.ConvertToVapi(node, mpmodel.EdgeNodeBindingType())

if errs != nil {
log.Printf("Failed to convert node object, errors are %v", errs)
return nil, errs[0]
if errs != nil {
log.Printf("Failed to convert node object, errors are %v", errs)
return nil, errs[0]
}
nodeDeploymentInfo = dataValue.(*data.StructValue)
}
nodeDeploymentInfo := dataValue.(*data.StructValue)

obj := mpmodel.TransportNode{
Description: &description,
Expand All @@ -725,6 +745,37 @@ func resourceNsxtEdgeTransportNodeCreate(d *schema.ResourceData, m interface{})
connector := getPolicyConnector(m)
client := nsx.NewTransportNodesClient(connector)

nodeID := d.Get("node_id").(string)
if nodeID != "" {
obj, err := client.Get(nodeID)
if err != nil {
return handleCreateError("TransportNode", nodeID, err)
}
// Set node_id, revision and computed values in schema
d.Set("failure_domain", obj.FailureDomainId)

converter := bindings.NewTypeConverter()
base, errs := converter.ConvertToGolang(obj.NodeDeploymentInfo, mpmodel.EdgeNodeBindingType())
if errs != nil {
return handleCreateError("TransportNode", nodeID, errs[0])
}
node := base.(mpmodel.EdgeNode)
d.Set("external_id", node.ExternalId)
d.Set("fqdn", node.Fqdn)
d.Set("ip_addresses", node.IpAddresses)
if obj.Revision != nil {
d.Set("revision", obj.Revision)
}

d.SetId(nodeID)
err = resourceNsxtEdgeTransportNodeUpdate(d, m)
if err != nil {
// There is a failure in update, let's discard this so state will remain clean
d.SetId("")
}
return err
}

obj, err := getTransportNodeFromSchema(d, m)
if err != nil {
return err
Expand Down Expand Up @@ -914,23 +965,31 @@ func getCPUConfigFromSchema(cpuConfigList []interface{}) []mpmodel.CpuCoreConfig
func getHostSwitchProfileResourceType(m interface{}, id string) (string, error) {
connector := getPolicyConnector(m)
client := infra.NewHostSwitchProfilesClient(connector)
structValue, err := client.Get(id)

// we retrieve a list of profiles instead of using Get(), as the id could be either MP id or Policy id
list, err := client.List(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
if err != nil {
return "", err
}

converter := bindings.NewTypeConverter()
baseInterface, errs := converter.ConvertToGolang(structValue, model.PolicyBaseHostSwitchProfileBindingType())
if errs != nil {
return "", errs[0]
}
base := baseInterface.(model.PolicyBaseHostSwitchProfile)
resourceType, ok := mpHostSwitchProfileTypeFromPolicyType[base.ResourceType]
if !ok {
return "", fmt.Errorf("MP resource type not found for %s", base.ResourceType)

for _, structValue := range list.Results {
baseInterface, errs := converter.ConvertToGolang(structValue, model.PolicyBaseHostSwitchProfileBindingType())
if errs != nil {
return "", errs[0]
}
base := baseInterface.(model.PolicyBaseHostSwitchProfile)

if *base.Id == id || *base.RealizationId == id {
resourceType, ok := mpHostSwitchProfileTypeFromPolicyType[base.ResourceType]
if !ok {
return "", fmt.Errorf("MP resource type not found for %s", base.ResourceType)
}
return resourceType, nil
}
}
return resourceType, nil

return "", fmt.Errorf("Host Switch Profile type not found for %s", id)
}

func getHostSwitchProfileIDsFromSchema(m interface{}, hswProfileList []interface{}) ([]mpmodel.HostSwitchProfileTypeIdEntry, error) {
Expand Down Expand Up @@ -1236,6 +1295,7 @@ func resourceNsxtEdgeTransportNodeRead(d *schema.ResourceData, m interface{}) er
d.Set("description", obj.Description)
d.Set("display_name", obj.DisplayName)
setMPTagsInSchema(d, obj.Tags)
d.Set("node_id", obj.NodeId)
d.Set("failure_domain", obj.FailureDomainId)

if obj.HostSwitchSpec != nil {
Expand Down
2 changes: 1 addition & 1 deletion website/docs/d/transport_node_realization.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ resource "nsxt_transport_node" "test" {
}
}
node_settings {
hostname = "tf_edge_node"
hostname = "tf-edge-node"
allow_ssh_root_login = true
enable_ssh = true
}
Expand Down
35 changes: 34 additions & 1 deletion website/docs/r/edge_transport_node.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ resource "nsxt_edge_transport_node" "test" {
}
}
node_settings {
hostname = "tf_edge_node"
hostname = "tf-edge-node"
allow_ssh_root_login = true
enable_ssh = true
}
Expand All @@ -56,13 +56,46 @@ resource "nsxt_edge_transport_node" "test" {
**NOTE:** `data.vsphere_network`, `data.vsphere_compute_cluster`, `data.vsphere_datastore`, `data.vsphere_host` are
obtained using [hashicorp/vsphere](https://registry.terraform.io/providers/hashicorp/vsphere/) provider.

## Example Usage, with Edge Transport Node created externally and converted into a transport node using Terraform
```hcl
data "nsxt_transport_node" "test_node" {
display_name = "tf_edge_node"
}
resource "nsxt_edge_transport_node" "test_node" {
node_id = data.nsxt_transport_node.test_node.id
description = "Terraform-deployed edge node"
display_name = "tf_edge_node"
standard_host_switch {
ip_assignment {
static_ip_pool = data.nsxt_policy_ip_pool.vtep_ip_pool.realized_id
}
transport_zone_endpoint {
transport_zone = data.nsxt_transport_zone.overlay_tz.id
}
transport_zone_endpoint {
transport_zone = data.nsxt_transport_zone.vlan_tz.id
}
host_switch_profile = [data.nsxt_policy_uplink_host_switch_profile.edge_uplink_profile.path]
pnic {
device_name = "fp-eth0"
uplink_name = "uplink1"
}
}
node_settings {
hostname = "tf-edge-node"
}
}
```

## Argument Reference

The following arguments are supported:

* `display_name` - (Required) Display name of the resource.
* `description` - (Optional) Description of the resource.
* `tag` - (Optional) A list of scope + tag pairs to associate with this resource.
* `node_id` - (Optional) The id of a pre-deployed Edge appliance to be converted into a transport node. Note that `node_id` attribute conflicts with `external_id`, `fqdn`, `ip_addresses` `deployment_config` and `node_settings` and those will be ignored while specifying `node_id`.
* `failure_domain` - (Optional) Id of the failure domain.
* `standard_host_switch` - (Required) Standard host switch specification.
* `host_switch_id` - (Optional) The host switch id. This ID will be used to reference a host switch.
Expand Down

0 comments on commit 204cd2b

Please sign in to comment.