diff --git a/environments/.stackhpc/terraform/main.tf b/environments/.stackhpc/terraform/main.tf index 872003db3..b76fca322 100644 --- a/environments/.stackhpc/terraform/main.tf +++ b/environments/.stackhpc/terraform/main.tf @@ -54,6 +54,10 @@ variable "volume_backed_instances" { default = false } +variable "root_volume_size" { + default = 15 +} + variable "k3s_token" { type = string } @@ -76,7 +80,9 @@ module "cluster" { k3s_token = var.k3s_token login_nodes = { - login-0: var.other_node_flavor + login-0 = { + flavor: var.other_node_flavor + } } compute = { standard: { # NB: can't call this default! @@ -84,6 +90,7 @@ module "cluster" { flavor: var.other_node_flavor compute_init_enable: ["compute", "etc_hosts", "nfs", "basic_users", "eessi"] } + # Example of how to add another partition: # extra: { # nodes: ["compute-2", "compute-3"] @@ -92,6 +99,7 @@ module "cluster" { } volume_backed_instances = var.volume_backed_instances + root_volume_size = var.root_volume_size environment_root = var.environment_root # Can reduce volume size a lot for short-lived CI clusters: diff --git a/environments/skeleton/{{cookiecutter.environment}}/terraform/compute.tf b/environments/skeleton/{{cookiecutter.environment}}/terraform/compute.tf index a90108924..c8a2b22f4 100644 --- a/environments/skeleton/{{cookiecutter.environment}}/terraform/compute.tf +++ b/environments/skeleton/{{cookiecutter.environment}}/terraform/compute.tf @@ -27,4 +27,5 @@ module "compute" { k3s_token = var.k3s_token control_address = [for n in openstack_compute_instance_v2.control["control"].network: n.fixed_ip_v4 if n.access_network][0] security_group_ids = [for o in data.openstack_networking_secgroup_v2.nonlogin: o.id] + baremetal_nodes = data.external.baremetal_nodes.result } diff --git a/environments/skeleton/{{cookiecutter.environment}}/terraform/compute/nodes.tf b/environments/skeleton/{{cookiecutter.environment}}/terraform/compute/nodes.tf index 9bb75466e..bc96c926f 100644 --- a/environments/skeleton/{{cookiecutter.environment}}/terraform/compute/nodes.tf +++ b/environments/skeleton/{{cookiecutter.environment}}/terraform/compute/nodes.tf @@ -83,6 +83,8 @@ resource "openstack_compute_instance_v2" "compute" { {for e in var.compute_init_enable: e => true} ) + availability_zone = var.match_ironic_node ? "${var.availability_zone}::${var.baremetal_nodes[each.key]}" : null + user_data = <<-EOF #cloud-config fqdn: ${var.cluster_name}-${each.key}.${var.cluster_name}.${var.cluster_domain_suffix} diff --git a/environments/skeleton/{{cookiecutter.environment}}/terraform/compute/variables.tf b/environments/skeleton/{{cookiecutter.environment}}/terraform/compute/variables.tf index b0e489017..571ced0e5 100644 --- a/environments/skeleton/{{cookiecutter.environment}}/terraform/compute/variables.tf +++ b/environments/skeleton/{{cookiecutter.environment}}/terraform/compute/variables.tf @@ -1,6 +1,9 @@ +# NB: Only variables which may be set directly on the compute group are +# have descriptions here (and defaults if optional) - others are just passed in + variable "nodes" { type = list(string) - description = "list of node names for partition" + description = "List of node names for this compute group" } variable "flavor" { @@ -14,7 +17,6 @@ variable "cluster_name" { variable "cluster_domain_suffix" { type = string - default = "invalid" } variable "cluster_net_id" { @@ -27,41 +29,35 @@ variable "cluster_subnet_id" { variable "key_pair" { type = string - description = "Name of an existing keypair in OpenStack" } variable "image_id" { type = string - description = "ID of image for the partition" + description = "ID of image for this compute node group" } variable "environment_root" { type = string - description = "Path to environment root, automatically set by activate script" } variable "vnic_type" { type = string - description = "VNIC type, see https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/networking_port_v2#vnic_type" + description = "VNIC type for this compute group, see https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/networking_port_v2#vnic_type" default = "normal" } variable "vnic_profile" { type = string - description = "VNIC binding profile as json string, see https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/networking_port_v2#profile." + description = "VNIC binding profile for this compute group as json string, see https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/networking_port_v2#profile." default = "{}" } variable "volume_backed_instances" { - description = "Whether to use volumes for root disks" type = bool - default = false } variable "root_volume_size" { - description = "Size of volume for root volumes if using volume backed instances, in Gb" type = number - default = 40 } variable "extra_volumes" { @@ -84,6 +80,23 @@ variable "k3s_token" { type = string } +variable "match_ironic_node" { + description = "Whether to launch instances on the Ironic node of the same name as this cluster node" + type = bool + default = false + +} + +variable availability_zone { + description = "Name of availability zone - ignored unless match_ironic_node is true" + type = string + default = "nova" +} + +variable "baremetal_nodes" { + type = map(string) +} + variable "control_address" { description = "Name/address of control node" type = string @@ -93,4 +106,4 @@ variable "compute_init_enable" { type = list(string) description = "Groups to activate for ansible-init compute rebuilds" default = [] -} \ No newline at end of file +} diff --git a/environments/skeleton/{{cookiecutter.environment}}/terraform/nodes.tf b/environments/skeleton/{{cookiecutter.environment}}/terraform/nodes.tf index 8ea8cabcb..e091ae003 100644 --- a/environments/skeleton/{{cookiecutter.environment}}/terraform/nodes.tf +++ b/environments/skeleton/{{cookiecutter.environment}}/terraform/nodes.tf @@ -1,5 +1,24 @@ locals { control_volumes = concat([openstack_blockstorage_volume_v3.state], var.home_volume_size > 0 ? [openstack_blockstorage_volume_v3.home][0] : []) + + login_node_defaults = { + availability_zone = "nova" + match_ironic_node = false + } + + login_nodes = { + for nodename, cfg in var.login_nodes: + nodename => merge(local.login_node_defaults, cfg) + } +} + +data "external" "baremetal_nodes" { + # returns an empty map if cannot list baremetal nodes + program = ["bash", "-c", <<-EOT + openstack baremetal node list --limit 0 -f json 2>/dev/null | \ + jq -r 'try map( { (.Name|tostring): .UUID } ) | add catch {}' || echo '{}' + EOT + ] } resource "openstack_networking_port_v2" "login" { @@ -99,11 +118,11 @@ resource "openstack_compute_instance_v2" "control" { resource "openstack_compute_instance_v2" "login" { - for_each = var.login_nodes + for_each = local.login_nodes name = "${var.cluster_name}-${each.key}" image_id = var.cluster_image_id - flavor_name = each.value + flavor_name = each.value.flavor key_pair = var.key_pair dynamic "block_device" { @@ -129,6 +148,8 @@ resource "openstack_compute_instance_v2" "login" { control_address = [for n in openstack_compute_instance_v2.control["control"].network: n.fixed_ip_v4 if n.access_network][0] } + availability_zone = each.value.match_ironic_node ? "${each.value.availability_zone}::${data.external.baremetal_nodes.result[each.key]}" : null + user_data = <<-EOF #cloud-config fqdn: ${var.cluster_name}-${each.key}.${var.cluster_name}.${var.cluster_domain_suffix} diff --git a/environments/skeleton/{{cookiecutter.environment}}/terraform/variables.tf b/environments/skeleton/{{cookiecutter.environment}}/terraform/variables.tf index bdffd40ce..0279af019 100644 --- a/environments/skeleton/{{cookiecutter.environment}}/terraform/variables.tf +++ b/environments/skeleton/{{cookiecutter.environment}}/terraform/variables.tf @@ -31,7 +31,14 @@ variable "control_node_flavor" { variable "login_nodes" { type = map - description = "Mapping defining login nodes: key -> (str) nodename suffix, value -> (str) flavor name" + description = <<-EOF + Mapping defining login nodes. Keys are the node name suffix. Values are a mapping as follows: + Required: + flavor: String flavor name + Optional: + match_ironic_node: Bool, whether to launch instances on the Ironic node of the same name as this cluster node + availability_zone: String, name of availability zone - ignored unless match_ironic_node is true + EOF } variable "cluster_image_id" {