diff --git a/INSTALL_GUIDE.md b/INSTALL_GUIDE.md new file mode 100644 index 0000000..8e535c0 --- /dev/null +++ b/INSTALL_GUIDE.md @@ -0,0 +1,94 @@ + +# Manual Installation + +## Step 1: Deploy L2 Gateway + +Assuming we don't have another gateway deployed, we'll need to create one. + + +We'll be using the helper scripts developed for running the test suite for Nutanix to simplify this installation. + +```sh +#!/bin/bash + +export EMAPI_AUTH_TOKEN= +export L2GATEWAY_VLAN_DESCRIPTION=ntnx-demo + +curl https://artifacts.platformequinix.com/images/nutanix/misc/scripts/install-l2gateway.sh | sh 2>&1 | tee /root/install-l2gw.log +``` + +## Step 2: Deploy one or more Nutanix Nodes (m3.xlarge) + +## Step 3: Once installation is complete, move nodes to L2 mode + +## Step 4: Reboot Nutanix nodes (to allow them to re-dhcp from new l2 gateway) + +## Step 5: Discover CVM IPs + +Look at the lease table on the dhcp server, and find all the kvm mac leases. + +```sh +curl -s http://192.168.0.1/leases +``` + +## Step 5: Login to Nutanix CVM node and create cluster + +```sh +ssh nutanix@$CVM_IP +cluster -s "CVM_IP1,CVM_IP2,CVM_IP3" create +``` + +## Step 6: Access Prism's UI + +Open `https://$CVM_IP:9440` in your browser + +Default login is `admin` and `nutanix/4u` + +A password change will be required, we'll use `Nutanix.123` + +Follow the account steps. + +### Spawning a VM + +#### Step 1: Configure DNS + +Settings -> Name Servers +Add +8.8.8.8 + +#### Step 2: Add image + +Settings -> Image Configuration +Upload Image + +Name: Rocky8 +Type: ISO +URL: + +#### Step 3: Configure a network + +Settings -> Network Configuration + +Create Network + +Network Name: vlan0 +VLAN ID: 0 + +#### Step 4: Create VM + +Settings -> VM + +Create VM + +Name: rocky8 +vCPU(s): 8 +Memory: 8 + +Disks +CDROM: Edit, use rocky8 +Add New Disk: + Size: 100 + +Save + +Power on diff --git a/README.md b/README.md index e700a23..8959f8f 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,42 @@ -# terraform-equinix-template +# Nutanix Cluster on Equinix Metal - - -[![Experimental](https://img.shields.io/badge/Stability-Experimental-red.svg)](https://github.com/equinix-labs/standards#about-uniform-standards) -[![run-pre-commit-hooks](https://github.com/equinix-labs/terraform-equinix-template/actions/workflows/pre-commit.yaml/badge.svg)](https://github.com/equinix-labs/terraform-equinix-template/actions/workflows/pre-commit.yaml) -[![generate-terraform-docs](https://github.com/equinix-labs/terraform-equinix-template/actions/workflows/documentation.yaml/badge.svg)](https://github.com/equinix-labs/terraform-equinix-template/actions/workflows/documentation.yaml) +This Terraform module will deploy a demonstrative Nutanix Cluster in Layer 2 isolation on Equinix Metal. The cluster IPAM and Internet Access is managed by a Rocky bastion/gateway node. -`terraform-equinix-template` is a minimal Terraform module that utilizes [Terraform providers for Equinix](https://registry.terraform.io/namespaces/equinix) to provision digital infrastructure and demonstrate higher level integrations. +## Acronyms and Terms - +* AOS: Acropolis Operating System +* NOS: Nutanix Operating System (Used interchangably with AOS) +* AHV: AOS Hypervisor +* Phoenix: The AOS/NOS Installer +* CVM: Cluster Virtual Machine +* Prism: AOS Cluster Web UI -## Usage +## Nutanix Installation in a nutshell -This project is experimental and supported by the user community. Equinix does not provide support for this project. +For those who are unfamiliar with Nutanix. Nutanix is a virtual machine management suite, similar to VMWare ESXi. -Install Terraform using the [tfenv](https://github.com/tfutils/tfenv) utility. +Nutanix is typically deployed in a private network without public IPs assigned directly to the host. +This experience is different than what many cloud users would expect in an OS deployment. -This project may be forked, cloned, or downloaded and modified as needed as the base in your integrations and deployments. +Due to this, we'll be deploying Nutanix with only private management IPs and later converting the nodes to full Layer-2. -This project may also be used as a [Terraform module](https://learn.hashicorp.com/collections/terraform/modules). +To allow access to the internet and make it easier to access these hosts, we'll be deploying a server in Hybrid networking mode to act as a router and jump box. -To use this module in a new project, create a file such as: +To begin, we'll start by provisioning a c3.small which has two NICs. Allowing us to have one in layer-3 with a public IP, +and one in layer-2 to access the internal layer-2 network. -```hcl -# main.tf -terraform { - required_providers { - equinix = { - source = "equinix/equinix" - } -} +## Manual Installation -module "example" { - source = "github.com/equinix-labs/template" - # TEMPLATE: replace "template" with the name of the repo after the terraform-equinix- prefix. +See [INSTALL_GUIDE.md](INSTALL_GUIDE.md) to install by hand. Otherwise, skip to the following section to let Terraform do all the work. - # Published modules can be sourced as: - # source = "equinix-labs/template/equinix" - # See https://www.terraform.io/docs/registry/modules/publish.html for details. +## Terraform installation - # version = "0.1.0" - - # TEMPLATE: insert required variables here -} +```sh +terraform init +eval $(metal env -o terraform --export) +terraform apply ``` -Install [pre-commit](https://pre-commit.com/#install) with its prerequesites: [python](https://docs.python.org/3/using/index.html) and [pip](https://pip.pypa.io/en/stable/installation/). - -Configure pre-commit: `pre-commit install`. - -Install required packages: [tflint](https://github.com/terraform-linters/tflint), [tfsec](https://aquasecurity.github.io/tfsec/v1.0.11/getting-started/installation/), [shfmt](https://github.com/mvdan/sh), [shellcheck](https://github.com/koalaman/shellcheck), and [markdownlint](https://github.com/markdownlint/markdownlint). - -Run `terraform init -upgrade` and `terraform apply`. - -## Module Documentation - -The main README.md, the modules README.md and the examples README.md are populated by [terraform-docs worflow job](.github/workflows/documentation.yaml). The following sections are appended between the terraform-docs delimeters: Requiremenents, Providers, Modules, Resources, Inputs, and Outputs. - -## Module Release and Changelog Generation - -The module git release and [changelog](CHANGELOG.md) are generated by the [release workflow job](.github/workflows/release.yaml). The release worflow follows the [conventional commits convention](https://www.conventionalcommits.org/). To submit a commit, please follow the [commit message format guidelines](https://www.conventionalcommits.org/en/v1.0.0/#specification). This job is set to run manually by default. - -Example commit message: `fix: disabled log generation for system services` - -For more examples, please see [conventional commit message examples](https://www.conventionalcommits.org/en/v1.0.0/#examples). - ## Examples To view examples for how you can leverage this module, please see the [examples](examples/) directory. diff --git a/docs/template-doc.md b/docs/template-doc.md deleted file mode 100644 index 4378ae5..0000000 --- a/docs/template-doc.md +++ /dev/null @@ -1 +0,0 @@ -# template-doc diff --git a/examples/.keep b/examples/.keep new file mode 100644 index 0000000..e69de29 diff --git a/examples/simple/README.md b/examples/simple/README.md deleted file mode 100644 index 6a41ec3..0000000 --- a/examples/simple/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Simple Example - -This example demonstrates usage of the Equinix Template module. - -## Usage - -```bash -terraform init -terraform apply -``` - - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.3 | - -## Providers - -No providers. - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [example](#module\_example) | ../../ | n/a | - -## Resources - -No resources. - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [example\_metal\_auth\_token](#input\_example\_metal\_auth\_token) | The example auth token value defines what will be included in the example resource in main.tf. This example is descriptive. | `string` | n/a | yes | -| [example\_metal\_project\_id](#input\_example\_metal\_project\_id) | The example project id value defines what will be included in the example resource in main.tf. This example is descriptive. | `string` | n/a | yes | - -## Outputs - -| Name | Description | -|------|-------------| -| [example\_device\_hostname](#output\_example\_device\_hostname) | The example output. In practice, output value reference implicit resource attributes declared in main.tf | -| [example\_gateway\_id](#output\_example\_gateway\_id) | The example output. In practice, output value reference implicit resource attributes declared in main.tf | - diff --git a/examples/simple/main.tf b/examples/simple/main.tf deleted file mode 100644 index 43c115c..0000000 --- a/examples/simple/main.tf +++ /dev/null @@ -1,12 +0,0 @@ -terraform { - required_version = ">= 1.3" -} - -module "example" { - # TEMPLATE: Replace this path with the Git repo path or Terraform Registry path - source = "../../" - - # Define any required variables - metal_project_id = var.example_metal_project_id - metal_auth_token = var.example_metal_auth_token -} diff --git a/examples/simple/outputs.tf b/examples/simple/outputs.tf deleted file mode 100644 index 50d8eb0..0000000 --- a/examples/simple/outputs.tf +++ /dev/null @@ -1,24 +0,0 @@ -# TEMPLATE: Consider the attributes users of this module will need to take advantage of this module -# TEMPLATE: in a new module that depends on this module (addresses, credentials, filenames). -# TEMPLATE: All outputs must have a description. Do not include descriptions or help text in the -# TEMPLATE: value, use the description field. -# TEMPLATE: -# TEMPLATE: Declare all outputs in this file, sprawling declarations are difficult to identify. -# TEMPLATE: -# TEMPLATE: https://www.terraform.io/docs/language/values/outputs.html -# TEMPLATE: https://www.terraform.io/docs/language/expressions/types.html -# TEMPLATE: - -# TEMPLATE: Replace sample output described below with your own. -output "example_device_hostname" { - description = "The example output. In practice, output value reference implicit resource attributes declared in main.tf" - sensitive = false - value = module.example.device_hostname -} - -# TEMPLATE: Replace sample output described below with your own. -output "example_gateway_id" { - description = "The example output. In practice, output value reference implicit resource attributes declared in main.tf" - sensitive = false - value = module.example.gateway_id -} diff --git a/examples/simple/variables.tf b/examples/simple/variables.tf deleted file mode 100644 index 878ff3d..0000000 --- a/examples/simple/variables.tf +++ /dev/null @@ -1,21 +0,0 @@ -# TEMPLATE: All variables must have a description and should declare their type. -# TEMPLATE: Set defaults whenever possible but do not set defaults for required properties. -# TEMPLATE: Declare all variables in this file, sprawling declarations are difficult to identify. -# TEMPLATE: -# TEMPLATE: https://www.terraform.io/docs/language/values/variables.html -# TEMPLATE: https://www.terraform.io/docs/language/expressions/types.html -# TEMPLATE: - -# TEMPLATE: Replace sample variable described below with your own. -variable "example_metal_project_id" { - type = string - description = "The example project id value defines what will be included in the example resource in main.tf. This example is descriptive." - sensitive = false -} - -# TEMPLATE: Replace sample variable described below with your own. -variable "example_metal_auth_token" { - type = string - description = "The example auth token value defines what will be included in the example resource in main.tf. This example is descriptive." - sensitive = true -} diff --git a/files/static-file.txt b/files/static-file.txt deleted file mode 100644 index 69ac7b4..0000000 --- a/files/static-file.txt +++ /dev/null @@ -1 +0,0 @@ -# TEMPLATE: Place your static files referenced but not executed by Terraform under this folder diff --git a/helpers/helper-script.sh b/helpers/helper-script.sh deleted file mode 100644 index 8bb98bb..0000000 --- a/helpers/helper-script.sh +++ /dev/null @@ -1 +0,0 @@ -# TEMPLATE: Place your bespoke script NOT called by Terraform here. diff --git a/main.tf b/main.tf index 5eb376a..2289688 100644 --- a/main.tf +++ b/main.tf @@ -1,50 +1,47 @@ -# TEMPLATE: Before using "provider" blocks, consider https://www.terraform.io/docs/language/modules/develop/providers.html#implicit-provider-inheritance -# TEMPLATE: -# TEMPLATE: All ".tf" files are parsed at once. There is no benefit to numerically prefixed filenames. Keep all resource definitions in "main.tf". -# TEMPLATE: -# TEMPLATE: When main.tf becomes unwieldy, consider submodules (https://www.terraform.io/docs/language/modules/develop/structure.html) -# TEMPLATE: and dependency inversion (https://www.terraform.io/docs/language/modules/develop/composition.html). -# TEMPLATE: - -# TEMPLATE: Replace sample provider described below with your own. -terraform { - required_version = ">= 1.3" - - provider_meta "equinix" { - # TEMPLATE: Replace the module name with your own. - module_name = "template" - } - - required_providers { - equinix = { - source = "equinix/equinix" - version = ">= 1.8.0" - } - } +data "equinix_metal_project" "nutanix" { + name = "devrel-marques-testing" } -# TEMPLATE: Replace sample provider described below with your own. -provider "equinix" { - auth_token = var.metal_auth_token +resource "equinix_metal_vlan" "test" { + project_id = data.equinix_metal_project.nutanix.id + description = var.metal_vlan_description + metro = "da" } -# TEMPLATE: Replace sample resource described below with your own. -resource "equinix_metal_device" "example_device" { - hostname = "example-device" +resource "equinix_metal_device" "bastion" { + project_id = data.equinix_metal_project.nutanix.id + hostname = "bastion" + user_data = templatefile("bastion-userdata.tmpl", { + metal_auth_token = var.metal_auth_token + metal_vlan_description = var.metal_vlan_description + }) + operating_system = "rocky_9" plan = "c3.small.x86" - metro = "sv" - operating_system = "ubuntu_20_04" - billing_cycle = "hourly" - project_id = var.metal_project_id + metro = "da" } -# TEMPLATE: Run `terraform get` to install local module -# TEMPLATE: Run `terraform init` to initialize backends and install plugins -# TEMPLATE: Replace sample in-line local module described below with your own. -# TEMPLATE -module "inline_module" { - source = "./modules/inline-module" +resource "equinix_metal_port" "bastion_bond0" { + port_id = [for p in equinix_metal_device.bastion.ports : p.id if p.name == "bond0"][0] + layer2 = false + bonded = true + vlan_ids = [equinix_metal_vlan.test.id] +} - # Define any required variables - inline_module_project_id = var.metal_project_id +resource "equinix_metal_device" "nutanix" { + count = 1 + project_id = data.equinix_metal_project.nutanix.id + hostname = "nutanix-devrel-test-{count.index}" + user_data = templatefile("nutanix-userdata.tmpl", {}) + operating_system = "nutanix_lts_6_5" + plan = "m3.large.x86" + metro = "da" } + +resource "equinix_metal_port" "nutanix_bond0" { + for_each = equinix_metal_device.nutanix + port_id = [for p in each.value.ports : p.id if p.name == "bond0"][0] + layer2 = true + bonded = true + vlan_ids = [equinix_metal_vlan.test.id] +} + diff --git a/modules/inline-module/README.md b/modules/inline-module/README.md deleted file mode 100644 index 52db438..0000000 --- a/modules/inline-module/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# In-line Module - -This example demonstrates usage of an in-line module. - -## Usage - -```bash -terraform init -terraform apply -``` - - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.3 | -| [equinix](#requirement\_equinix) | >= 1.8.0 | - -## Providers - -| Name | Version | -|------|---------| -| [equinix](#provider\_equinix) | >= 1.8.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [equinix_metal_gateway.inline_module_gateway](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/metal_gateway) | resource | -| [equinix_metal_vlan.inline_module_vlan](https://registry.terraform.io/providers/equinix/equinix/latest/docs/resources/metal_vlan) | resource | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [inline\_module\_project\_id](#input\_inline\_module\_project\_id) | The example project id value defines what will be included in the example resource in main.tf. This example is descriptive. | `string` | n/a | yes | - -## Outputs - -| Name | Description | -|------|-------------| -| [metal\_gateway\_id](#output\_metal\_gateway\_id) | The example output. In practice, output value reference implicit resource attributes declared in main.tf | - diff --git a/modules/inline-module/main.tf b/modules/inline-module/main.tf deleted file mode 100644 index 65dcb41..0000000 --- a/modules/inline-module/main.tf +++ /dev/null @@ -1,37 +0,0 @@ -# TEMPLATE: Before using "provider" blocks, consider https://www.terraform.io/docs/language/modules/develop/providers.html#implicit-provider-inheritance -# TEMPLATE: -# TEMPLATE: All ".tf" files are parsed at once. There is no benefit to numerically prefixed filenames. Keep all resource definitions in "main.tf". -# TEMPLATE: -# TEMPLATE: When main.tf becomes unwieldy, consider submodules (https://www.terraform.io/docs/language/modules/develop/structure.html) -# TEMPLATE: and dependency inversion (https://www.terraform.io/docs/language/modules/develop/composition.html). - -# TEMPLATE: Replace sample provider described below with your own. -terraform { - required_version = ">= 1.3" - - provider_meta "equinix" { - # TEMPLATE: Replace the module name with your own. - module_name = "inline-module" - } - - required_providers { - equinix = { - source = "equinix/equinix" - version = ">= 1.8.0" - } - } -} - -# TEMPLATE: Replace sample resource described below with your own. -resource "equinix_metal_vlan" "inline_module_vlan" { - description = "VLAN in SV" - metro = "sv" - project_id = var.inline_module_project_id -} - -# TEMPLATE: Replace sample resource described below with your own. -resource "equinix_metal_gateway" "inline_module_gateway" { - project_id = var.inline_module_project_id - vlan_id = equinix_metal_vlan.inline_module_vlan.id - private_ipv4_subnet_size = 8 -} diff --git a/modules/inline-module/outputs.tf b/modules/inline-module/outputs.tf deleted file mode 100644 index 58deb9d..0000000 --- a/modules/inline-module/outputs.tf +++ /dev/null @@ -1,17 +0,0 @@ -# TEMPLATE: Consider the attributes users of this module will need to take advantage of this module -# TEMPLATE: in a new module that depends on this module (addresses, credentials, filenames). -# TEMPLATE: All outputs must have a description. Do not include descriptions or help text in the -# TEMPLATE: value, use the description field. -# TEMPLATE: -# TEMPLATE: Declare all outputs in this file, sprawling declarations are difficult to identify. -# TEMPLATE: -# TEMPLATE: https://www.terraform.io/docs/language/values/outputs.html -# TEMPLATE: https://www.terraform.io/docs/language/expressions/types.html -# - -# TEMPLATE: Replace sample output described below with your own. -output "metal_gateway_id" { - description = "The example output. In practice, output value reference implicit resource attributes declared in main.tf" - sensitive = false - value = equinix_metal_gateway.inline_module_gateway.id -} diff --git a/modules/inline-module/variables.tf b/modules/inline-module/variables.tf deleted file mode 100644 index 770ebc4..0000000 --- a/modules/inline-module/variables.tf +++ /dev/null @@ -1,14 +0,0 @@ -# TEMPLATE: All variables must have a description and should declare their type. -# TEMPLATE: Set defaults whenever possible but do not set defaults for required properties. -# TEMPLATE: Declare all variables in this file, sprawling declarations are difficult to identify. -# TEMPLATE: -# TEMPLATE: https://www.terraform.io/docs/language/values/variables.html -# TEMPLATE: https://www.terraform.io/docs/language/expressions/types.html -# - -# TEMPLATE: Replace sample variable described below with your own. -variable "inline_module_project_id" { - type = string - description = "The example project id value defines what will be included in the example resource in main.tf. This example is descriptive." - sensitive = false -} diff --git a/outputs.tf b/outputs.tf index 4bdfbc6..e69de29 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,24 +0,0 @@ -# TEMPLATE: Consider the attributes users of this module will need to take advantage of this module -# TEMPLATE: in a new module that depends on this module (addresses, credentials, filenames). -# TEMPLATE: All outputs must have a description. Do not include descriptions or help text in the -# TEMPLATE: value, use the description field. -# TEMPLATE: -# TEMPLATE: Declare all outputs in this file, sprawling declarations are difficult to identify. -# TEMPLATE: -# TEMPLATE: https://www.terraform.io/docs/language/values/outputs.html -# TEMPLATE: https://www.terraform.io/docs/language/expressions/types.html -# TEMPLATE: - -# TEMPLATE: Replace sample output described below with your own. -output "device_hostname" { - description = "The example output. In practice, output value reference implicit resource attributes declared in main.tf" - sensitive = false - value = equinix_metal_device.example_device.hostname -} - -# TEMPLATE: Replace sample output described below with your own. -output "gateway_id" { - description = "The example output. In practice, output value reference implicit resource attributes declared in main.tf" - sensitive = false - value = module.inline_module.metal_gateway_id -} diff --git a/providers.tf b/providers.tf new file mode 100644 index 0000000..f800550 --- /dev/null +++ b/providers.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + equinix = { + source = "equinix/equinix" + } + } +} + +# Configure the Equinix Metal Provider. +provider "equinix" { + auth_token = var.metal_auth_token +} diff --git a/scripts/template-script.sh b/scripts/template-script.sh deleted file mode 100644 index ac9101c..0000000 --- a/scripts/template-script.sh +++ /dev/null @@ -1 +0,0 @@ -# TEMPLATE: Place your beskope script called by Terraform here. diff --git a/templates/bastion-userdata.tftpl b/templates/bastion-userdata.tftpl new file mode 100644 index 0000000..8573a76 --- /dev/null +++ b/templates/bastion-userdata.tftpl @@ -0,0 +1,8 @@ +#!/bin/bash + +export EMAPI_AUTH_TOKEN=${var.metal_auth_token} +export L2GATEWAY_VLAN_DESCRIPTION=${var.metal_vlan_description} + +curl https://artifacts.platformequinix.com/images/nutanix/misc/scripts/install-l2gateway.sh | sh 2>&1 | tee /root/install-l2gw.log + + diff --git a/templates/nutanix-userdata.tftpl b/templates/nutanix-userdata.tftpl new file mode 100644 index 0000000..139597f --- /dev/null +++ b/templates/nutanix-userdata.tftpl @@ -0,0 +1,2 @@ + + diff --git a/templates/template-file.tftpl b/templates/template-file.tftpl deleted file mode 100644 index b87e591..0000000 --- a/templates/template-file.tftpl +++ /dev/null @@ -1 +0,0 @@ -# TEMPLATE: Place your template here. This will be called by the templatefile function. For more info: https://developer.hashicorp.com/terraform/language/functions/templatefile diff --git a/variables.tf b/variables.tf index 5334a3c..e05a9e6 100644 --- a/variables.tf +++ b/variables.tf @@ -1,21 +1,6 @@ -# TEMPLATE: All variables must have a description and should declare their type. -# TEMPLATE: Set defaults whenever possible but do not set defaults for required properties. -# TEMPLATE: Declare all variables in this file, sprawling declarations are difficult to identify. -# TEMPLATE: -# TEMPLATE: https://www.terraform.io/docs/language/values/variables.html -# TEMPLATE: https://www.terraform.io/docs/language/expressions/types.html -# TEMPLATE: - -# TEMPLATE: Replace sample variable described below with your own. -variable "metal_project_id" { - type = string - description = "The example project id value defines what will be included in the example resource in main.tf. This example is descriptive." - sensitive = false +variable "metal_auth_token" { } -# TEMPLATE: Replace sample variable described below with your own. -variable "metal_auth_token" { - type = string - description = "The example auth token value defines what will be included in the example resource in main.tf. This example is descriptive." - sensitive = true +variable "metal_vlan_description" { + default = "ntnx-demo" }