Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add support for Edge transport nodes which were created externally #1473

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 71 additions & 27 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"},
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably a bad idea, but I'll suggest it anyway.
As this resource is not generated, we do not have to fully respect the NSX API schema.

Perhaps we can move node_id in deployment_config and specify that it conflictsWith vm_deployment_config?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deployment_config block contains stuff which is related to the Edge VM deployment: also attributes which are outside the vm_deployment_config block is related, e.g form_factor. Also node_user_settings is related.
And as these are required for a non-pre-deployed, we'll have to change those to optional for pre-deployed edges.
In fact I think that it's a good idea to mark deployment_config and node_id as conflicting.

"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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please clarify - even when specifying node_id, user still needs to populate node_deployment_info? What properties are needed?
I don't think node_deployment_info is Computed

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resource has no node_deployment_info attribute as it's not implementing the entire API, as host nodes are created with policy API. Relevant node_deployment_info attributes are placed under deployment_config, and directly under the resource root.
I've set these attributes as conflicting with node_id.

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.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be helpful to also add a log statement here for debugging purposes.

e.g: "nodeID not specified, will deploy edge using values in deploymentConfig"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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,29 @@ 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 handleReadError(d, "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)

d.SetId(nodeID)
return resourceNsxtEdgeTransportNodeUpdate(d, m)
}

obj, err := getTransportNodeFromSchema(d, m)
if err != nil {
return err
Expand Down Expand Up @@ -1236,6 +1279,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
Loading