diff --git a/modules_iaas/cloud_instance/main.tf b/modules_iaas/cloud_instance/main.tf new file mode 100644 index 0000000..f0a3fe6 --- /dev/null +++ b/modules_iaas/cloud_instance/main.tf @@ -0,0 +1,494 @@ + +variable "tenant" { + description = "Tenant informations" + type = any + default = { + prefix = "" + } +} + +variable "instance" { + description = "Instances definitions" + type = any + default = [] +} + + +variable "instance_defaults" { + description = "Instances default definitions" + type = any + default = {} +} + +variable "catalog" { + description = "Existing catalog" + type = any + default = {} +} + + + +// variable "networks_catalog" { +// description = "Known networks to find domain" +// type = any +// default = {} +// } + +// variable "images_catalog" { +// description = "Available OS images" +// type = any +// default = {} +// } +// variable "oses_catalog" { +// description = "Available falvors" +// type = any +// default = {} +// } + +// variable "flavors_catalog" { +// description = "Available falvors" +// type = any +// default = {} +// } + + + + + +resource "local_file" "config_cloud" { + content = yamlencode(var.catalog) + filename = "out/ns-${var.tenant.ns}-${var.instance.name}-catalogs.yml" +} + + +resource "local_file" "config_deployment" { + content = yamlencode(var.instance) + count = local.number + filename = "out/ns-${var.tenant.ns}-${var.instance.name}-deployment-${count.index}.yml" +} + + +locals { + + number = "${var.instance.number}" + name = "${var.tenant.prefix}${var.instance.name}" + + + // default_catalog = { + // oses = { + // "cirros_0.5" = { + // format = "img" + // url = "https://download.cirros-cloud.net/0.5.2/cirros-0.5.2-x86_64-disk.img" + // }, + // } + // } + + default_instance = { + "name" = null + os = "default" + flavor = "default" + vcpu = 1 + memory = 2048 + disk_gb = null + cloudinit_file = null + cloudinit_vars = {} + metadata = {} + } + default_os = { + image = null + disk_gb = 1 + cloudinit_file = null + cloudinit_vars = {} + memory = 512 + metadata = {} + } + default_flavor = { + name = "default" + vcpu = 1 + memory = 2048 + disk_gb = null + metadata = {} + } + + disks = [] + + # Ensure struct + instance = merge(local.default_instance, var.instance_defaults, var.instance) + resolved_flavor = merge(local.default_flavor, var.catalog.flavors[local.instance.flavor]) + resolved_os = merge(local.default_os, var.catalog.oses[local.instance.os]) + + # Intermediate + cloudinit_file = coalesce(local.instance.cloudinit_file, local.resolved_os.cloudinit_file) + cloudinit_vars = merge(local.resolved_os.cloudinit_vars, local.instance.cloudinit_vars) + // cloudinit_file = local.resolved_os.cloudinit_file + + + vcpu = coalesce(local.instance.vcpu, local.resolved_flavor.vcpu) + memory = coalesce(local.instance.memory, local.resolved_flavor.memory, local.resolved_os.memory, 10) + disk_gb = coalesce(local.instance.disk_gb, local.resolved_flavor.disk_gb, local.resolved_os.disk_gb, 10) + + + metadata = merge(local.resolved_flavor.metadata, local.resolved_os.metadata, local.instance.metadata) + + // cloudinit_file = coalesce(try(local.instance.cloudinit_file), try(local.resolved_os.cloudinit_file)) + + +} + + +resource "local_file" "config_instance" { + content = yamlencode(local.resolved_os) + filename = "out/instance-${var.tenant.prefix}${local.name}-deployment.yml" +} + + +module "virt_instance" { + source = "../../modules_libvirt/virt_instance" + +// for_each = { for v in var.instances : v.name => merge(local.instances_defaults, v) } + + name = local.name + number = local.number + + networks = local.instance.networks + + // instance_pool = null + + # From Oses + // cloudinit_userdata = "My Template" + cloudinit_vars = local.cloudinit_vars + image_base_file = "debian_12-1532.qcow2" + image_base_pool = "default" + + cloudinit_file = local.cloudinit_file + metadata = local.metadata + +// disks = local.instance.disks + + + # From flavors +// vcpu +// memory +// disk_gb = disk + + +// domain = local.domain + +// prefix = each.value.prefix +// name_fqdn = each.value.name_fqdn +// metadata = each.value.metadata + +// domain = each.value.domain +// number = each.value.number +// instance_pool = each.value.instance_pool + + +// images = each.value.images +// os = each.value.os +// image_base_pool = each.value.image_base_pool +// image_base_file = each.value.image_base_file +// disk_gb = each.value.disk_gb + +// flavor = each.value.flavor +// memory = each.value.memory +// vcpu = each.value.vcpu +// disks = each.value.disks +// volumes = each.value.volumes +// networks = each.value.networks +// wait_for_lease = each.value.wait_for_lease + +// user = each.value.user +// authorized_key = each.value.authorized_key +// password_hash = each.value.password_hash + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// # Variables +// # ===================== + +// variable "name" { +// description = "Instance name" +// type = string +// } +// variable "domain" { +// description = "Instance domain" +// type = string +// default = "" +// } +// variable "number" { +// description = "Number of instances" +// type = number +// default = 1 +// } + + + + + + +// variable "os" { +// description = "Instance os" +// type = string +// default = "cirros_0.5" +// } + + +// variable "authorized_key" { +// description = "Default user authorized key" +// type = string +// default = "" +// } + +// # Note: This should only be used for debugging purpose +// variable "password_hash" { +// description = "Default user password hash (ie: $1$SaltSalt$GhE887kYCerthShgxern00)" +// type = string +// default = "" +// # sensitive = true +// } + + + + + +// # Prefix to use ??? +// variable "prefix" { +// description = "String to prefix each instances" +// type = string +// default = "" +// } + +// variable "name_fqdn" { +// description = "Define instance name with fqdn" +// type = bool +// default = false +// } +// variable "name_prefix" { +// description = "Define instance name with project prefix" +// type = bool +// default = true +// } +// variable "instance_pool" { +// description = "Volume pool for instance disks" +// type = string +// default = "default" +// } + +// variable "disk_gb" { +// description = "Size in gb of the root disk" +// type = number +// default = 2 +// } + + + +// # +// variable "flavor" { +// description = "Instance flavor" +// type = string +// default = "x-small" +// } +// variable "memory" { +// description = "Instance memory" +// type = string +// default = "2048" +// } +// variable "vcpu" { +// description = "Number of vcpu" +// type = number +// default = 2 +// } + + + +// variable "images" { +// description = "Available OS images" +// type = any +// default = {} +// } + + +// variable "image_base_pool" { +// description = "Default image pool" +// type = string +// default = "default" +// } +// variable "image_base_file" { +// description = "Image base file in the image_pool" +// type = string +// default = "TEST__ MISSING" +// } + + +// variable "disks" { +// description = "Ephemeral disks list" +// type = list +// default = [] +// } +// variable "volumes" { +// description = "Persistant volume list" +// type = list +// default = [] +// } +// variable "networks" { +// description = "Network list" +// type = list +// default = [] +// } + +// variable "wait_for_lease" { +// description = "Wait for network ip assignment" +// type = bool +// default = false +// } + +// variable "metadata" { +// description = "Metadata do add in state" +// type = any +// default = {} +// } + + +// # Cloud init +// # ----------- + +// variable "user" { +// description = "Default user login" +// type = string +// default = "cloud" +// } + + + +// // locals { +// // pre_default = { +// // network = "default" +// // domain = "no_domain22" +// // } +// // default_network = try(var.networks_catalog[var.instances_defaults.default_network], null) != null ? var.instances_defaults.default_network : local.pre_default.network + +// // networks = local.default_network != null ? [{ name = "${var.tenant.prefix}${local.default_network}" }] : [] +// // domain = try( var.networks_catalog[local.default_network].domain, local.pre_default.domain) + +// // # Recommended default flavor +// // default = { +// // number = 1 +// // instance_pool = "default" +// // flavor = "default" + +// // # From images ! +// // images = {} +// // disk_gb = 14 +// // os = "cirros_0.5" +// // memory = 1024 +// // user = "" +// // image_base_pool = "default" +// // image_base_file = "missing.qcow2" +// // metadata = {} + +// // vcpu = 1 +// // disks = [] +// // volumes = [] +// // name_fqdn = true +// // wait_for_lease = false +// // password_hash = "" +// // authorized_key = "" + +// // prefix = var.tenant.prefix +// // default_network = local.default_network +// // networks = local.networks +// // domain = local.domain +// // } +// // instances_defaults = merge(local.default, var.instances_defaults) +// // } + + + + + + + +// # Resources +// # ===================== + + +// // module "cloud_instance" { +// // source = "../../modules/virt_instance" +// // for_each = { for v in var.instances : v.name => merge(local.instances_defaults, v) } + +// // name = each.value.name +// // prefix = each.value.prefix +// // name_fqdn = each.value.name_fqdn +// // metadata = each.value.metadata + +// // domain = each.value.domain +// // number = each.value.number + + +// // // instance_pool = each.value.instance_pool + + +// // // images = each.value.images +// // // os = each.value.os +// // // image_base_pool = each.value.image_base_pool +// // // image_base_file = each.value.image_base_file +// // // disk_gb = each.value.disk_gb + +// // // flavor = each.value.flavor +// // // memory = each.value.memory +// // // vcpu = each.value.vcpu +// // // disks = each.value.disks +// // // volumes = each.value.volumes +// // // networks = each.value.networks +// // // wait_for_lease = each.value.wait_for_lease + +// // // user = each.value.user +// // // authorized_key = each.value.authorized_key +// // // password_hash = each.value.password_hash + +// // } + + +// // # Outputs +// // # ===================== +// // output "instances" { +// // value = module.cloud_instance +// // } diff --git a/modules_iaas/cloud_instance/provider.tf b/modules_iaas/cloud_instance/provider.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules_iaas/cloud_instances/main.tf b/modules_iaas/cloud_instances/main.tf new file mode 100644 index 0000000..602b378 --- /dev/null +++ b/modules_iaas/cloud_instances/main.tf @@ -0,0 +1,142 @@ + +# Variables +# ===================== + +variable "tenant" { + description = "Tenant informations" + type = any + default = { + prefix = "" + } +} + +variable "instance_defaults" { + description = "Instances default definitions" + type = any + default = {} +} + +// variable "networks_catalog" { +// description = "Known networks to find domain" +// type = any +// default = {} +// } + +// variable "images_catalog" { +// description = "Available images" +// type = any +// default = {} +// } + +// variable "oses_catalog" { +// description = "Available OS images" +// type = any +// default = {} +// } + +// variable "flavors_catalog" { +// description = "Available falvors" +// type = any +// default = {} +// } + +variable "catalog" { + description = "Existing catalog" + type = any + default = {} +} + +variable "instances" { + description = "Instances definitions" + type = any + default = [] +} + + + + + +# Resources +# ===================== + +// locals { +// pre_default = { +// network = "default" +// domain = "no_domain22" +// } +// default_network = try(var.networks_catalog[var.instances_defaults.default_network], null) != null ? var.instances_defaults.default_network : local.pre_default.network + +// networks = local.default_network != null ? [{ name = "${var.tenant.prefix}${local.default_network}" }] : [] +// domain = try( var.networks_catalog[local.default_network].domain, local.pre_default.domain) + +// # Recommended default flavor +// default = { +// number = 1 +// instance_pool = "default" +// flavor = "default" + +// # From images ! +// images = {} +// disk_gb = 14 +// os = "cirros_0.5" +// memory = 1024 +// user = "" +// image_base_pool = "default" +// image_base_file = "missing.qcow2" +// metadata = {} + +// vcpu = 1 +// disks = [] +// volumes = [] +// name_fqdn = true +// wait_for_lease = false +// password_hash = "" +// authorized_key = "" + +// prefix = var.tenant.prefix +// default_network = local.default_network +// networks = local.networks +// domain = local.domain +// } +// instances_defaults = merge(local.default, var.instances_defaults) +// } + + +module "cloud_instances" { + source = "../cloud_instance" + + for_each = { for v in var.instances : v.name => merge(var.instance_defaults, v) } + + // count = var.instances + + tenant = var.tenant + instance_defaults = var.instance_defaults + + + catalog = var.catalog + // networks_catalog = var.networks_catalog + // images_catalog = var.images_catalog + // oses_catalog = var.oses_catalog + // flavors_catalog = var.flavors_catalog + + // instance = var.instances[count.index] + instance = each.value +} + + + + +# Outputs +# ===================== +output "instances" { + value = module.cloud_instances +} + +# output "DEBUG" { +# value = { +# NORMAL = module.virt_instances +# INSTANCES = var.instances +# INSTANCES_defaults = var.instances_defaults +# } +# } + diff --git a/modules_iaas/cloud_instances/provider.tf b/modules_iaas/cloud_instances/provider.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules_iaas/iaas_inv_ansible/_OLD/example.inv.yml b/modules_iaas/iaas_inv_ansible/_OLD/example.inv.yml new file mode 100644 index 0000000..38b78d4 --- /dev/null +++ b/modules_iaas/iaas_inv_ansible/_OLD/example.inv.yml @@ -0,0 +1,14 @@ +--- +terraform: + hosts: + mail.example.com: + children: + webservers: + hosts: + foo.example.com: + bar.example.com: + dbservers: + hosts: + one.example.com: + two.example.com: + three.example.com: diff --git a/modules_iaas/iaas_inv_ansible/_OLD/inventory.tmpl b/modules_iaas/iaas_inv_ansible/_OLD/inventory.tmpl new file mode 100644 index 0000000..1d7c6ab --- /dev/null +++ b/modules_iaas/iaas_inv_ansible/_OLD/inventory.tmpl @@ -0,0 +1,6 @@ +${ ip_addrs } +%{ for addr in ip_addrs ~} +backend ${addr}:xx +%{ endfor ~} + + diff --git a/modules_iaas/iaas_inv_ansible/_OLD/main.tf b/modules_iaas/iaas_inv_ansible/_OLD/main.tf new file mode 100644 index 0000000..a6f3da7 --- /dev/null +++ b/modules_iaas/iaas_inv_ansible/_OLD/main.tf @@ -0,0 +1,13 @@ +variable "payload" { + description = "Payload informations" + type = any + default = {} +} + +resource "local_file" "ansible_inventory" { + filename = "inventory.txt" + content = templatefile("${path.module}/inventory.tmpl", { ip_addrs = ["10.0.0.1", "10.0.0.2"] } + ) +} +# tutu = ["bli", "blaaah"] + #toto = "${var.payload}" diff --git a/modules_iaas/iaas_inv_ansible/_OLD/tpl_example.tpl b/modules_iaas/iaas_inv_ansible/_OLD/tpl_example.tpl new file mode 100644 index 0000000..bad3d4c --- /dev/null +++ b/modules_iaas/iaas_inv_ansible/_OLD/tpl_example.tpl @@ -0,0 +1,17 @@ +[cfg] +%{ for index, group in ansible_group_cfg ~} +${ hostname_cfg[index] } ${ index == 0 ? "mongodb_primary=True" : "" } +%{ endfor ~} + +%{ for shard_index in number_of_shards ~} +[shard${shard_index}] +%{ for index, group in ansible_group_shards ~} +${ group == tostring(shard_index) && ansible_group_index[index] == "0" ? join(" ", [ hostname_shards[index], "mongodb_primary=True\n" ]) : "" ~} +${ group == tostring(shard_index) && ansible_group_index[index] != "0" ? join("", [ hostname_shards[index], "\n" ]) : "" ~} +%{ endfor ~} +%{ endfor ~} + +[mongos] +%{ for index, group in ansible_group_mongos ~} +${hostname_mongos[index]} +%{ endfor ~} diff --git a/modules_iaas/iaas_inv_ansible/ansible_host/main.tf b/modules_iaas/iaas_inv_ansible/ansible_host/main.tf new file mode 100644 index 0000000..28c2236 --- /dev/null +++ b/modules_iaas/iaas_inv_ansible/ansible_host/main.tf @@ -0,0 +1,42 @@ + +#variable "hostname" { +# description = "hostname" +# type = any +# default = {} +#} +#variable "groups" { +# description = "groups" +# type = any +# default = {} +#} +#variable "vars" { +# description = "Variables" +# type = any +# default = {} +#} + +resource "ansible_host" "example2" { + inventory_hostname = "www.example.com" + groups = ["web"] + vars = { + foo = "bar" + } +} + + +resource "ansible_host" "example" { + inventory_hostname = "example.com" + groups = ["web"] + vars = { + ansible_user = "admin" + } +} + +resource "ansible_group" "web" { + inventory_group_name = "web" + children = ["foo", "bar", "baz"] + vars = { + foo = "bar" + bar = 2 + } +} diff --git a/modules_iaas/iaas_inv_ansible/ansible_host/provider.tf b/modules_iaas/iaas_inv_ansible/ansible_host/provider.tf new file mode 100644 index 0000000..45b51b8 --- /dev/null +++ b/modules_iaas/iaas_inv_ansible/ansible_host/provider.tf @@ -0,0 +1,9 @@ + +terraform { + required_providers { + ansible = { + source = "nbering/ansible" + version = "1.0.4" + } + } +} diff --git a/modules_iaas/iaas_inv_ansible/main.tf b/modules_iaas/iaas_inv_ansible/main.tf new file mode 100644 index 0000000..17ca06a --- /dev/null +++ b/modules_iaas/iaas_inv_ansible/main.tf @@ -0,0 +1,34 @@ + +variable "payload" { + description = "Instance description" + type = any + default = {} +} + + +resource "ansible_host" "example2" { + inventory_hostname = "www.example.com" + groups = ["web"] + vars = { + foo = "bar" + } +} + + +resource "ansible_host" "example" { + inventory_hostname = "example.com" + groups = ["web"] + vars = { + ansible_user = "admin" + } +} + +resource "ansible_group" "web" { + inventory_group_name = "web" + children = ["foo", "bar", "baz"] + vars = { + foo = "bar" + bar = 2 + } +} + diff --git a/modules_iaas/iaas_inv_ansible/provider.tf b/modules_iaas/iaas_inv_ansible/provider.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules_iaas/virt_deployment/main.tf b/modules_iaas/virt_deployment/main.tf new file mode 100644 index 0000000..5156a98 --- /dev/null +++ b/modules_iaas/virt_deployment/main.tf @@ -0,0 +1,183 @@ + +# Variables +# ===================== + +variable "tenant" { + description = "Tenant informations" + type = any + default = {} +} + +variable "deploy" { + description = "Deployments configuration" + type = map + default = {} +} + +variable "catalog" { + description = "Cloud informations" + type = any + default = {} +} + +variable "deployment" { + description = "Deployment options" + type = any + default = {} +} + + +# Resources +# ===================== + +locals { + + def_deploy = { + deployment_name = "default" + + pools = false + volumes = false + networks = false + instances = false + images = false + } + + + def_deployment = { + + # Default values (always obj) + // os = {} + pool = {} + image = {} + // flavor = {} + volume = {} + network = {} + instance = {} + + # Deployment config + oses = {} + pools = {} + images = {} + flavors = {} + volumes = [] # List of volumes + networks = {} + instances = [] # List of instances + } + + deploy = merge ( local.def_deploy, var.deploy ) + deployment = merge ( local.def_deployment, var.deployment ) + catalog = merge ( local.def_deployment, var.catalog ) + + merged_catalog = {for name, item in local.def_deployment: + # Merge only dicts, skip lists ... + name => try( + merge( + local.catalog[name], + local.deployment[name], + ), + [] + ) + } + +} + + + +# Modules +# ===================== + +module "cloud_pools" { + source = "../../modules_libvirt/virt_pools" + count = local.deploy.pools ? 1: 0 + + pools = local.deployment.pools + pools_defaults = local.deployment.pool +} + +module "cloud_images" { + source = "../virt_images" + count = local.deploy.images ? 1: 0 + depends_on = [module.cloud_pools] + + images = local.deployment.images + images_defaults = local.deployment.image +} + +module "cloud_volumes" { + source = "../virt_volumes" + count = local.deploy.volumes ? 1: 0 + depends_on = [module.cloud_pools] + + tenant = var.tenant + volumes = local.deployment.volumes + volumes_defaults = local.deployment.volume +} + +module "cloud_networks" { + source = "../virt_networks" + count = local.deploy.networks ? 1: 0 + + tenant = var.tenant + networks = local.deployment.networks + networks_defaults = local.deployment.network +} + +module "cloud_instances" { + source = "../cloud_instances" + count = local.deploy.instances ? 1: 0 + depends_on = [module.cloud_networks, module.cloud_images, module.cloud_volumes] + + tenant = var.tenant + instance_defaults = local.deployment.instance + + catalog = local.merged_catalog + + // networks_catalog = local.deployment.networks + // images_catalog = local.deployment.images + // oses_catalog = local.deployment.oses + // flavors_catalog = local.deployment.flavors + + instances = local.deployment.instances +} + + + +# Dump Config +# ===================== + +resource "local_file" "config_cloud" { + content = yamlencode(var.catalog) + filename = "out/ns-${local.deploy.deployment_name}-catalog.yml" +} + +resource "local_file" "config_deployment" { + content = yamlencode(var.deployment) + filename = "out/ns-${local.deploy.deployment_name}-deployment.yml" +} + + + + +# Output +# ===================== + +// output "cloud_pools" { +// value = module.cloud_pools +// } + +// output "cloud_images" { +// value = module.cloud_images +// } +// output "cloud_volumes" { +// value = module.cloud_volumes +// } +// output "cloud_networks" { +// value = module.cloud_networks +// } +// output "cloud_instances" { +// value = module.cloud_instances +// } + +// output "DEBUG" { +// value = local.deployment +// } diff --git a/modules_iaas/virt_deployment/provider.tf b/modules_iaas/virt_deployment/provider.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules_iaas/virt_deployments/main.tf b/modules_iaas/virt_deployments/main.tf new file mode 100644 index 0000000..96b6bfe --- /dev/null +++ b/modules_iaas/virt_deployments/main.tf @@ -0,0 +1,81 @@ + +# Variables +# ===================== + +variable "tenant" { + description = "Tenant informations" + type = any + default = {} +} + +variable "deploy" { + description = "Deployment options" + type = any + default = {} +} + +variable "catalog" { + description = "CAtalog informations" + type = any + default = {} +} + +variable "deployment_defaults" { + description = "Default stack" + type = any + default = {} +} + +variable "deployments" { + description = "Deployments configuration" + type = any + default = {} +} + + +# Resources +# ===================== + +locals { + + # Fetch stack name + deployment_name = try( + var.deploy.deployment_name, + "default" + ) + # Proceed to chained merges + deployment = merge( + var.deployment_defaults, + try( + var.deployments[local.deployment_name], {} + ) + ) +} + +module "virt_deployment" { + source = "../virt_deployment" + + tenant = var.tenant + deploy = merge(var.deploy, {deployment_name = local.deployment_name}) + + catalog = var.catalog + deployment = local.deployment +} + +#module "iaas_inv_ansible" { +# source = "../iaas_inv_ansible" +# payload = var.config +#} + + + +# Output +# ===================== +// output "namespace" { +// value = module.virt_namespace +// } +output "deployment" { + value = local.deployment +} + + diff --git a/modules_iaas/virt_deployments/provider.tf b/modules_iaas/virt_deployments/provider.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules_iaas/virt_flavor/main.tf b/modules_iaas/virt_flavor/main.tf new file mode 100644 index 0000000..bdea7e5 --- /dev/null +++ b/modules_iaas/virt_flavor/main.tf @@ -0,0 +1,93 @@ + +# Flavors +# ======================== + +variable "flavor" { + description = "The flavor to use" + type = string + default = "small" +} + +# Default flavors +locals { + + + flavors = { + "default" = { + "cpu" = 2, + "ram" = 1024, + "disk" = 5 + }, + + "tiny" = { + "cpu" = 1, + "ram" = 512, + "disk" = 5 + }, + "tiny_ram" = { + "cpu" = 1, + "ram" = 1024, + "disk" = 10 + }, + "small" = { + "cpu" = 2, + "ram" = 2048, + "disk" = 10 + }, + "small_ram" = { + "cpu" = 2, + "ram" = 4096, + "disk" = 10 + }, + "medium" = { + "cpu" = 4, + "ram" = 4096, + "disk" = 10 + }, + "medium_ram" = { + "cpu" = 4, + "ram" = 6144, + "disk" = 10 + }, + "large" = { + "cpu" = 6, + "ram" = 6144, + "disk" = 20 + }, + "large_ram" = { + "cpu" = 6, + "ram" = 8192, + "disk" = 10 + }, + "huge" = { + "cpu" = 8, + "ram" = 8192, + "disk" = 10 + }, + "huge_ram" = { + "cpu" = 8, + "ram" = 12288, + "disk" = 10 + } + } +} + +locals { + flavor = local.flavors[var.flavor] + cpu = local.flavor.cpu + ram = local.flavor.ram + disk = local.flavor.disk +} +output "flavor" { + value = local.flavor +} +output "cpu" { + value = local.cpu +} +output "disk" { + value = local.disk +} +output "ram" { + value = local.ram +} + diff --git a/modules_iaas/virt_flavor/provider.tf b/modules_iaas/virt_flavor/provider.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules_iaas/virt_images/main.tf b/modules_iaas/virt_images/main.tf new file mode 100644 index 0000000..3aace0c --- /dev/null +++ b/modules_iaas/virt_images/main.tf @@ -0,0 +1,54 @@ + +# Variables +# ===================== + +variable "images" { + description = "Images definitions" + type = any + default = {} +} + +variable "images_defaults" { + description = "Images default definitions" + type = map + default = { + pool = "default", + } +} + +# Resources +# ===================== + +locals { + images = flatten( + [for dist_name, dist_config in var.images: + { + name = dist_name + + format = try(dist_config.format, "") + file = try(dist_config.file, "${dist_name}.${dist_config.format}" , basename( dist_config.url )) + url = dist_config.url + + pool = try(dist_config.pool, var.images_defaults.pool, "default") + } + ] + ) +} + +module "volumes" { + source = "../virt_volumes" + volumes = local.images + volumes_defaults = var.images_defaults +} + + +# Outputs +# ===================== + +output "images" { + value = local.images +} + +output "volumes" { + value = module.volumes.volumes +} diff --git a/modules_iaas/virt_images/provider.tf b/modules_iaas/virt_images/provider.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules_iaas/virt_networks/main.tf b/modules_iaas/virt_networks/main.tf new file mode 100644 index 0000000..630ece0 --- /dev/null +++ b/modules_iaas/virt_networks/main.tf @@ -0,0 +1,73 @@ + +# Variables +# ===================== + +variable "tenant" { + description = "Tenant informations" + type = any + default = { + prefix = "" + } +} + +variable "networks" { + description = "Networks definitions" + type = any + default = {} +} + +variable "networks_defaults" { + description = "Network default definitions" + type = any + default = {} +} + + + +# Resources +# ===================== + +locals { + defaults = { + name_prefix = var.tenant.prefix + #interface_prefix = "vibr_${var.tenant.prefix}" + #interface_prefix = "vibr_" + interface_prefix = null + mode = "nat" + domain = "local" + gw4 = "192.168.100.1" + mask = "24" + + nat_enable = false + vip = [] + subnets = [] + } + networks_defaults = merge(local.defaults, var.networks_defaults) +} + +module "cloud_networks" { + source = "../../modules/virt_network" + for_each = { for k, v in var.networks : k => merge(local.networks_defaults, v) } + + name = "${each.value.name_prefix}${each.key}" + bridge = each.value.interface_prefix == null ? "" : substr("${each.value.interface_prefix}${each.key}", 0 , 15) + + mode = each.value.mode + domain = each.value.domain + gw4 = each.value.gw4 + mask = each.value.mask + + nat_enable = each.value.nat_enable + vip = each.value.vip + subnets = each.value.subnets +} + + +# Output +# ===================== + +output "networks" { + value = module.cloud_networks +} + + diff --git a/modules_iaas/virt_networks/provider.tf b/modules_iaas/virt_networks/provider.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules_iaas/virt_os/main.tf b/modules_iaas/virt_os/main.tf new file mode 100644 index 0000000..5d877ab --- /dev/null +++ b/modules_iaas/virt_os/main.tf @@ -0,0 +1,194 @@ + +# Instance System +# ======================== + + +# Variables +variable "os_name" { + description = "The name of the operating system to create" + type = string + default = "ubuntu" +} + +variable "os_version" { + description = "The version of the operationg system to use" + type = string + default = "latest" +} + +# ======== + +variable "cloud_init" { + type = string + default = "" + description = "Override system cloud_init" +} +variable "cloud_init_vars" { + type = map + default = {} + description = "Override cloud init vars" +} +# ========== + +variable "domain" { + type = string + default = "nodomain" +} + +variable "hostname" { + type = string + default = "noname" +} + +variable "user" { + type = string + default = "cloud" +} + +variable "password_hash" { + type = string + default = "" +} + +variable "authorized_key" { + type = string + default = "" +} + + + +locals { + # Operating System + # ======================== + distro = { + "debian" = { + "latest" = { + "cloud_init" = "debian", + "url" = "https://cdimage.debian.org/cdimage/cloud/buster/20210208-542/debian-10-genericcloud-amd64-20210208-542.qcow2", + }, + "10" = { + "cloud_init" = "debian", + "url" = "https://cdimage.debian.org/cdimage/cloud/buster/20210208-542/debian-10-genericcloud-amd64-20210208-542.qcow2", + }, + "10-20210208-542" = { + "cloud_init" = "debian", + "url" = "https://cdimage.debian.org/cdimage/cloud/buster/20210208-542/debian-10-genericcloud-amd64-20210208-542.qcow2", + }, + }, +# "ubuntu" = { +# "latest" = { +# "cloud_init" = "debian", +# "url" = "https://cloud-images.ubuntu.com/releases/groovy/release/ubuntu-20.10-server-cloudimg-amd64-disk-kvm.img", +# }, +# "20.10" = { +# "cloud_init" = "debian", +# "url" = "https://cloud-images.ubuntu.com/releases/groovy/release/ubuntu-20.10-server-cloudimg-amd64-disk-kvm.img", +# }, +# "20.04" = { +# "cloud_init" = "debian", +# "url" = "https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-amd64-disk-kvm.img", +# }, +# "19.04" = { +# "cloud_init" = "debian", +# "url" = "https://cloud-images.ubuntu.com/releases/disco/release/ubuntu-19.04-server-cloudimg-amd64.img", +# }, +# "18.04" = { +# "cloud_init" = "debian", +# "url" = "https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.img", +# }, +# "17.04" = { +# "cloud_init" = "debian", +# "url" = "http://cloud-images-archive.ubuntu.com/releases/zesty/release-20171208/ubuntu-17.04-server-cloudimg-amd64.img", +# }, +# "16.04" = { +# "cloud_init" = "debian", +# "url" = "https://cloud-images.ubuntu.com/releases/xenial/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img" +# }, +# }, +# "centos" = { +# "latest" = { +# "cloud_init" = "centos", +# "url" = "https://cloud.centos.org/centos/8/x86_64/images/CentOS-8-GenericCloud-8.3.2011-20201204.2.x86_64.qcow2", +# }, +# "8" = { +# "cloud_init" = "centos", +# "url" = "https://cloud.centos.org/centos/8/x86_64/images/CentOS-8-GenericCloud-8.3.2011-20201204.2.x86_64.qcow2", +# }, +# "8-stream" = { +# "cloud_init" = "centos", +# "url" = "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20201217.0.x86_64.qcow2", +# }, +# "7" = { +# "cloud_init" = "centos", +# "url" = "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-2009.qcow2", +# }, +# "6" = { +# "cloud_init" = "centos", +# "url" = "https://cloud.centos.org/centos/6/images/CentOS-6-x86_64-GenericCloud.qcow2" +# }, +# } + } + + + # Cloud Init + # ======================== + templates = { + "centos" = { + "template" = "${path.module}/templates/cloud_centos_generic.yml", + "vars" = {} + }, + "debian" = { + "template" = "${path.module}/templates/cloud_debian_generic.yml", + "vars" = { + "authorized_key" = "", + "authorized_keys" = [], + "commands" = [], + "packages" = [], + "allow_passwords" = true, + } + } + } + + template_default = { + "authorized_key" = var.authorized_key, + "password_hash" = var.password_hash, + "user" = var.user, + "domain" = var.domain, + "hostname" = var.hostname, + } + +} + + +locals { + os = local.distro[var.os_name][var.os_version] + + # Generate template + cloud_template = try(local.templates[var.cloud_init], local.templates[local.os.cloud_init]) + template_vars = merge(local.cloud_template.vars, local.template_default, var.cloud_init_vars) +} + + +output "distro" { + value = local.distro +} + + +output "os" { + value = local.os +} + +output "cloud_init" { + value = local.cloud_template +} +output "template_vars" { + value = local.template_vars +} + + +output "template" { + #value = data.template_file.user_data.rendered + value = templatefile("${local.cloud_template.template}", local.template_vars) +} + + diff --git a/modules_iaas/virt_os/provider.tf b/modules_iaas/virt_os/provider.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules_iaas/virt_os/templates/cloud_debian_generic.yml b/modules_iaas/virt_os/templates/cloud_debian_generic.yml new file mode 100644 index 0000000..adb1177 --- /dev/null +++ b/modules_iaas/virt_os/templates/cloud_debian_generic.yml @@ -0,0 +1,57 @@ +#cloud-config + +preserve_hostname: false +hostname: ${hostname} +fqdn: ${hostname}.${domain} +# prefer_fqdn_over_hostname: true +%{ if allow_passwords ~} +ssh_pwauth: true +%{ endif ~} + +groups: + - wheel +users: +- name: ${user} + sudo: ALL=(ALL) NOPASSWD:ALL + shell: /bin/bash + groups: users, admin, sudo +%{ if allow_passwords ~} + passwd: "${password_hash}" +%{ endif ~} + lock_passwd: %{ if allow_passwords ~}false%{ else }true%{ endif } + ssh_authorized_keys: +%{ if authorized_key != "" ~} + - "${authorized_key}" +%{ endif ~} +%{ for key in authorized_keys ~} + - "${key}" +%{ endfor ~} + +packages: + - vim + - telnet + - bash-completion + - htop + - tree + - lvm2 +%{ for pkg in packages ~} + - ${pkg} +%{ endfor ~} + +bootcmd: + - echo "127.0.0.1 ${hostname}.${domain} ${hostname}" >> /etc/hosts + +runcmd: + - rm /etc/skel/.bashrc + - apt update + - apt install -y qemu-guest-agent ca-certificates + - systemctl enable qemu-guest-agent + - systemctl start qemu-guest-agent + - apt upgrade +%{ for cmd in commands ~} + - ${cmd} +%{ endfor ~} + - echo "$(date): Success" > /var/log/provisionned + +final_message: "The system is ready, after $UPTIME seconds" + diff --git a/modules_iaas/virt_volumes/main.tf b/modules_iaas/virt_volumes/main.tf new file mode 100644 index 0000000..ea8bb3c --- /dev/null +++ b/modules_iaas/virt_volumes/main.tf @@ -0,0 +1,74 @@ + +# Variables +# ============ + +variable "tenant" { + description = "Tenant informations" + type = map + default = { + prefix = "" + } +} + +variable "volumes" { + description = "List of volumes" + type = list(any) + default = [] +} + +variable "volumes_defaults" { + description = "Default settings for volumes" + type = map + default = {} +} + + +# Main +# ============ + + +locals { + defaults = { + prefix = var.tenant.prefix + pool = "default", + pool_dir = "/virt", + size_gb = 10, + format = "qcow2" + persistant = false + + base_pool = "default", + base_file = "", + + url = "" + file = "" + } + volumes_defaults = merge(local.defaults, var.volumes_defaults) +} + + +module "cloud_volume" { + source = "../../modules_libvirt/virt_volume" + for_each = { for v in var.volumes: v.name => merge(local.volumes_defaults, v) } + + name = "${each.value.prefix}${each.value.name}" + pool = each.value.pool + size_gb = each.value.size_gb + format = each.value.format + persistant = each.value.persistant + + base_pool = each.value.base_pool + base_file = each.value.base_file + + url = each.value.url + file = each.value.file + +} + + +# Output +# ============ + +output "volumes" { + value = module.cloud_volume +} + diff --git a/modules_iaas/virt_volumes/provider.tf b/modules_iaas/virt_volumes/provider.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules_libvirt/virt_cloudinit/main.tf b/modules_libvirt/virt_cloudinit/main.tf new file mode 100644 index 0000000..f8de128 --- /dev/null +++ b/modules_libvirt/virt_cloudinit/main.tf @@ -0,0 +1,98 @@ + +# Variables +# ============ + +variable "name" { + type = string +} +variable "pool" { + type = string + default = "default" +} + +variable "userdata" { + type = string + default = "" +} +variable "metadata" { + type = string + default = "" +} +variable "networks" { + type = string + default = "" +} + +# Main +# ============ + +# Cloud init ISO +resource "libvirt_cloudinit_disk" "volume_cloudinit" { + name = var.name + pool = var.pool + user_data = var.userdata + meta_data = var.metadata + network_config = var.networks +} + +# Outputs +# ============ + +output "volume" { + value = libvirt_cloudinit_disk.volume_cloudinit +} + + + + + + +# ==================== DEPRECATED !!!! + + +# variable "baked_image" { +# type = bool +# default = true +# } +# +# variable "image_name" { +# type = string +# } +# +# variable "image_pool" { +# type = string +# } +# variable "image_dir" { +# type = string +# } +# variable "image_url" { +# type = string +# } + +# Deprecated by cloud_volume ! +# # Baked volume +# resource "libvirt_volume" "volume_system_baked" { +# name = "${var.prefix}system.qcow2" +# count = var.baked_image ? 1 : 0 +# pool = var.pool +# +# base_volume_name = var.image_name +# base_volume_pool = var.image_pool +# size = var.disk_size_gb * 1024 * 1024 * 1024 +# } +# +# # Copied volume +# resource "libvirt_volume" "volume_system_full" { +# name = "${var.prefix}system.qcow2" +# count = var.baked_image ? 0 : 1 +# pool = var.pool +# +# #source = var.baked_image ? "" : "${var.image_dir}/${var.image_name}" +# source = var.image_url +# } +# + + +#output "volume_os" { +# value = var.baked_image ? resource.libvirt_volume.volume_system_baked[0] : resource.libvirt_volume.volume_system_full[0] +#} diff --git a/modules_libvirt/virt_cloudinit/provider.tf b/modules_libvirt/virt_cloudinit/provider.tf new file mode 100644 index 0000000..7557e9b --- /dev/null +++ b/modules_libvirt/virt_cloudinit/provider.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + version = "0.6.14" + } + } +} diff --git a/modules_libvirt/virt_instance/main.tf b/modules_libvirt/virt_instance/main.tf new file mode 100644 index 0000000..2eb1132 --- /dev/null +++ b/modules_libvirt/virt_instance/main.tf @@ -0,0 +1,528 @@ + + + +# See: +# https://github.com/sk4zuzu/vm-pool/blob/master/terraform/redhat/nodes/domain.tf + # SEE: https://github.com/SUSE/ha-sap-terraform-deployments/blob/master/libvirt/modules/hana_node/main.tf#L21 + +#variable "tenant_networks" { +# type = any +# default = {} +#} + + + + +# Variables +# ===================== + +variable "name" { + description = "Instance name" + type = string +} +variable "domain" { + description = "Instance domain" + type = string + default = "localdomain" +} +variable "number" { + description = "Number of instances" + type = number + default = 1 +} + +# +// variable "prefix" { +// description = "String to prefix each instances" +// type = string +// default = "" +// } +variable "name_fqdn" { + description = "Use fqdn to name instance ?" + type = bool + default = false +} +// variable "name_prefix" { +// description = "Define instance name with project prefix" +// type = bool +// default = true +// } +variable "instance_pool" { + description = "Volume pool for instance disks" + type = string + default = "default" +} + +variable "disk_gb" { + description = "Size in gb of the root disk" + type = number + default = 2 +} + +# +variable "flavor" { + description = "Instance flavor" + type = string + default = "x-small" +} +variable "memory" { + description = "Instance memory" + type = string + default = "2048" +} +variable "vcpu" { + description = "Number of vcpu" + type = number + default = 2 +} + + + +variable "images" { + description = "Available OS images" + type = any + default = {} +} + +variable "os" { + description = "Instance os" + type = string + default = "cirros_0.5" +} + +variable "image_base_pool" { + description = "Default image pool" + type = string + default = "default" +} +variable "image_base_file" { + description = "Image base file in the image_pool" + type = string + default = "TEST__ MISSING" +} +variable "image_cloud_init" { + description = "Cloud init to use with this image" + type = string + default = "TEST__ CLOUDINIT CONFIG" +} +variable "cloudinit_userdata" { + description = "Cloud init userdata" + type = string + default = "TEST__ CLOUDINIT CONFIG222" +} + +variable "cloudinit_file" { + description = "Cloud init userdata" + type = string + default = "/non_existant" + nullable = false +} + +variable "cloudinit_vars" { + description = "Cloud init varaibles" + type = any + default = {} + nullable = false +} + + + + +variable "disks" { + description = "Ephemeral disks list" + type = list + default = [] +} +variable "volumes" { + description = "Persistant volume list" + type = list + default = [] +} +variable "networks" { + description = "Network list" + type = list + default = [] +} + +variable "wait_for_lease" { + description = "Wait for network ip assignment" + type = bool + default = null +} + +variable "metadata" { + description = "Metadata do add in state" + type = any + default = {} + nullable = false +} + + +# Cloud init +# ----------- + +variable "user" { + description = "Default user login" + type = string + default = "cloud" +} + +variable "authorized_key" { + description = "Default user authorized key" + type = string + default = "" +} + +# Note: This should only be used for debugging purpose +variable "password_hash" { + description = "Default user password hash (ie: $1$SaltSalt$GhE887kYCerthShgxern00)" + type = string + default = "" + # sensitive = true +} + + +# Cloud settings +# ===================== + +// module "system_os" { +// source = "../../modules/virt_os" +// count = var.number + +// os_name = "debian" +// os_version = "10" +// domain = var.domain +// hostname = "${var.name}${count.index}" +// user = var.user +// password_hash = var.password_hash +// authorized_key = var.authorized_key +// } + +// module "system_flavor" { +// source = "../../modules/virt_flavor" +// count = var.number +// flavor = var.flavor +// } + +# Volumes Configuration +# ===================== + +module "volume_cloudinit" { + source = "../virt_cloudinit" + count = var.number + + name = "inst_${var.name}${count.index}_cloudinit.iso" + pool = var.instance_pool + // userdata = module.system_os[count.index].template + // userdata = var.cloudinit_userdata + userdata = templatefile( + var.cloudinit_file, + merge( + local.parsed_cloudinit_vars, + { + hostname = "${local.vm_name}${count.index}" + fqdn = "${local.vm_name}${count.index}.${var.domain}" + instance_index = count.index + } + ) + ) + +} + +module "volume_os" { + source = "../virt_volume" + count = var.number + + format = "qcow2" + name = "inst_${var.name}${count.index}_os" + + pool = var.instance_pool + size_gb = var.disk_gb + base_pool = var.image_base_pool + # TOFIX: Hardcoded refrence to debian + #base_file = "debian_latest.qcow2" + // base_file = "TEST__${var.os} --" + base_file = var.image_base_file +} + +module "volumes_extra" { + source = "../virt_volumes" + count = var.number + + volumes = var.disks + volumes_defaults = { + prefix = "inst_${var.name}${count.index}_" + pool = var.instance_pool + # TOFIX: Hardcoded variable + pool_dir = "/virt" + } +} + + +# Instance +# ===================== + +locals { + hostname = replace( var.name, "_", "-") + vm_name = "${local.hostname}" + domain = var.domain + + vm_name_suffix = ( var.name_fqdn ? + ".${var.domain}" : + "" ) + vm_name_domain = ( var.domain != "" ? + ".${var.domain}" : + "" ) + + + metadata = merge( + { + domain = var.domain + // user = "FAILED" # var.user + // #password_hash = var.password_hash + // authorized_key = var.authorized_key + user = "cloud" + authorized_key = "" + authorized_keys = [] + timezone = "" + + instance_name = local.vm_name + instance_fqdn = "${local.vm_name}${local.vm_name_domain}" + + }, + var.metadata + ) + + cloudinit_vars = merge( + { + // hostname = "" + // domain = "" + // allow_passwords = false + // user = "" + // password_hash = "" + // authorized_key = "" + authorized_keys = [] + + packages = [] + commands = [] + }, + var.cloudinit_vars + ) + + parsed_cloudinit_vars = merge(local.metadata, local.cloudinit_vars) + + + default_net_interface = { + network_name = null + mac = null + addresses = null + hostname = null + + wait_for_lease = null + macvtap = null + vepa = null + } + networks = [for item in var.networks: + merge(local.default_net_interface, item) + ] + + + + // parsed_cloudinit = templatefile(var.cloudinit_file, local.cloudinit_vars) + + + // parsed_cloudinit = fileexists(var.cloudinit_file) ? templatefile(var.cloudinit_file, local.cloudinit_vars) : "NONE" + // parsed_cloudinit = try(templatefile(var.cloudinit_file, local.cloudinit_vars), null) + + // parsed_cloudinit = templatefile(var.cloudinit_file, local.cloudinit_vars) + // parsed_cloudinit = templatefile(var.cloudinit_file, merge(local.cloudinit_vars, local.metadata)) + // parsed_cloudinit = templatefile(var.cloudinit_file, local.parsed_cloudinit_vars) + + + + +} + + + +resource "local_file" "config_deployment" { + count = var.number + // content = yamlencode(local.parsed_cloudinit) + // content = yamlencode(local.parsed_cloudinit_vars) + content = yamlencode(resource.libvirt_domain.instdef) + // try(resource.libvirt_domain.instdef, null) + filename = "out/INST-${var.name}-${count.index}.yml" +} + + + + +resource "libvirt_domain" "instdef" { + count = var.number + + autostart = true + name = "${local.vm_name}${count.index}${local.vm_name_suffix}" + + // vm_name = ( var.name_fqdn ? + // "${local.vmname}${count.index}.${var.domain}" : + // "${local.vmname}${count.index}" ) + + description = "${local.vm_name}${count.index}${local.vm_name_domain}" + memory = var.memory + vcpu = var.vcpu + + // metadata = jsonencode(local.metadata) + metadata = jsonencode(merge( + local.metadata, + { + hostname = "${local.vm_name}${count.index}" + fqdn = "${local.vm_name}${count.index}.${var.domain}" + instance_index = count.index + } + ) + ) + + # Root FS + # ------------------- + cloudinit = module.volume_cloudinit[count.index].volume.id + disk { + volume_id = module.volume_os[count.index].volume.id + } + + # Instance disks + dynamic "disk" { + for_each = module.volumes_extra[count.index].volumes + content { + volume_id = disk.value.volume.id + #volume_id = disk.value.info.id + #volume_id = coalesce(disk.value.volume.id) + #file = coalesce(disk.value.info.id) + } + } + + + # Networking + # ------------------- + dynamic "network_interface" { + for_each = local.networks + content { + network_name = network_interface.value.name + + mac = try(network_interface.value.mac, null) + addresses = try(network_interface.value.addresses, null) + #hostname = try(network_interface.value.hostname, var.name) + + // wait for lease on first interface only by default + wait_for_lease = coalesce(network_interface.value.wait_for_lease, var.wait_for_lease, index(local.networks, network_interface.value) == 0 ? true: false ) + + macvtap = try(network_interface.value.macvtap, null) + vepa = try(network_interface.value.vepa, null) + } + } + + # Other + # ------------------- + console { + type = "pty" + target_port = "0" + target_type = "serial" + } + + console { + type = "pty" + target_type = "virtio" + target_port = "1" + } + + graphics { + type = "spice" + listen_type = "address" + autoport = true + } + + cpu { + mode = "host-passthrough" + } + +} + + +# Outputs +# ===================== + +output "instance" { + value = try(resource.libvirt_domain.instdef, null) +} + + +# output "volume_cloudinit" { +# value = module.volume_cloudinit +# } +// output "volume_os" { +// value = module.volume_os +// } +// output "volumes_extra" { +// value = module.volumes_extra +// } + +// # Tag infos +// output "system_os" { +// value = module.system_os +// } +// output "system_flavor" { +// value = module.system_flavor +// } + + + + + + + + + + + + + + + + +// resource "ansible_host" "ansible_def" { + +// count = var.number +// inventory_hostname = ( var.name_fqdn ? +// "${local.hostname}${count.index}.${var.domain}" : +// "${local.hostname}${count.index}" ) + +// #groups = [ +// # "role_${local.hostname}", +// # var.prefix != "" ? "prj_${var.prefix}" : "" +// # ] +// vars = { +// ansible_user = var.user +// ansible_host = "${local.hostname}${count.index}.${var.domain}" +// ansible_connection = "ssh" + +// instances_count = var.number +// instances_index = count.index + +// instance_domain = var.domain +// instance_hostname = "${local.hostname}${count.index}" +// instance_name = "${local.hostname}" + +// instance_provisionning_key = var.authorized_key +// instance_provisionning_user = var.user +// instance_flavor = var.flavor + +// instance_metadata = jsonencode(merge(local.metadata, var.metadata)) + +// # TOFIX: lol +// instance_os_name = "debian" +// instance_os_version = "10" + +// instance_vm = ( var.name_fqdn ? +// "${local.vmname}${count.index}.${var.domain}" : +// "${local.vmname}${count.index}" ) +// } +// } diff --git a/modules_libvirt/virt_instance/provider.tf b/modules_libvirt/virt_instance/provider.tf new file mode 100644 index 0000000..74925a4 --- /dev/null +++ b/modules_libvirt/virt_instance/provider.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + version = "0.6.14" + } + ansible = { + source = "nbering/ansible" + version = "~>1.0.4" + } + } +} diff --git a/modules_libvirt/virt_instance_FULL_BACK/main.tf b/modules_libvirt/virt_instance_FULL_BACK/main.tf new file mode 100644 index 0000000..5f791f8 --- /dev/null +++ b/modules_libvirt/virt_instance_FULL_BACK/main.tf @@ -0,0 +1,386 @@ + + + +# See: +# https://github.com/sk4zuzu/vm-pool/blob/master/terraform/redhat/nodes/domain.tf + # SEE: https://github.com/SUSE/ha-sap-terraform-deployments/blob/master/libvirt/modules/hana_node/main.tf#L21 + +#variable "tenant_networks" { +# type = any +# default = {} +#} + + + + +# Variables +# ===================== + +variable "name" { + description = "Instance name" + type = string +} +variable "domain" { + description = "Instance domain" + type = string + default = "" +} +variable "number" { + description = "Number of instances" + type = number + default = 1 +} + +# +variable "prefix" { + description = "String to prefix each instances" + type = string + default = "" +} +variable "name_fqdn" { + description = "Define instance name with fqdn" + type = bool + default = false +} +variable "name_prefix" { + description = "Define instance name with project prefix" + type = bool + default = true +} +variable "instance_pool" { + description = "Volume pool for instance disks" + type = string + default = "default" +} + +variable "disk_gb" { + description = "Size in gb of the root disk" + type = number + default = 2 +} + +# +variable "flavor" { + description = "Instance flavor" + type = string + default = "x-small" +} +variable "memory" { + description = "Instance memory" + type = string + default = "2048" +} +variable "vcpu" { + description = "Number of vcpu" + type = number + default = 2 +} + + + +variable "images" { + description = "Available OS images" + type = any + default = {} +} + +variable "os" { + description = "Instance os" + type = string + default = "cirros_0.5" +} + +variable "image_base_pool" { + description = "Default image pool" + type = string + default = "default" +} +variable "image_base_file" { + description = "Image base file in the image_pool" + type = string + default = "TEST__ MISSING" +} + + +variable "disks" { + description = "Ephemeral disks list" + type = list + default = [] +} +variable "volumes" { + description = "Persistant volume list" + type = list + default = [] +} +variable "networks" { + description = "Network list" + type = list + default = [] +} + +variable "wait_for_lease" { + description = "Wait for network ip assignment" + type = bool + default = false +} + +variable "metadata" { + description = "Metadata do add in state" + type = any + default = {} +} + + +# Cloud init +# ----------- + +variable "user" { + description = "Default user login" + type = string + default = "cloud" +} + +variable "authorized_key" { + description = "Default user authorized key" + type = string + default = "" +} + +# Note: This should only be used for debugging purpose +variable "password_hash" { + description = "Default user password hash (ie: $1$SaltSalt$GhE887kYCerthShgxern00)" + type = string + default = "" + # sensitive = true +} + + +# Cloud settings +# ===================== + +module "system_os" { + source = "../../modules/virt_os" + count = var.number + + os_name = "debian" + os_version = "10" + domain = var.domain + hostname = "${var.name}${count.index}" + user = var.user + password_hash = var.password_hash + authorized_key = var.authorized_key +} + +module "system_flavor" { + source = "../../modules/virt_flavor" + count = var.number + flavor = var.flavor +} + +# Volumes Configuration +# ===================== + +module "volume_cloudinit" { + source = "../virt_cloudinit" + count = var.number + + name = "inst_${var.prefix}${var.name}${count.index}_cloudinit.iso" + pool = var.instance_pool + userdata = module.system_os[count.index].template +} + +module "volume_os" { + source = "../virt_volume" + count = var.number + + format = "qcow2" + name = "inst_${var.prefix}${var.name}${count.index}" + + pool = var.instance_pool + size_gb = 42 + base_pool = var.image_base_pool + # TOFIX: Hardcoded refrence to debian + #base_file = "debian_latest.qcow2" + // base_file = "TEST__${var.os} --" + base_file = var.image_base_file +} + +module "volumes_extra" { + source = "../virt_volumes" + count = var.number + + volumes = var.disks + volumes_defaults = { + prefix = "inst_${var.prefix}${var.name}${count.index}_" + pool = var.instance_pool + # TOFIX: Hardcoded variable + pool_dir = "/virt" + } +} + + +# Instance +# ===================== + +locals { + hostname = replace( var.name, "_", "-") + vmname = ( var.name_prefix ? + "${var.prefix}${local.hostname}": + "${local.hostname}" ) + domain = var.domain + + metadata = { + #cloud_init = one(module.system_os[*].template) + os_name = "debian" + os_version = "10" + domain = var.domain + user = var.user + #password_hash = var.password_hash + authorized_key = var.authorized_key + } + + my_os_infos = var.images[var.os] +} + +resource "ansible_host" "ansible_def" { + + count = var.number + inventory_hostname = ( var.name_fqdn ? + "${local.hostname}${count.index}.${var.domain}" : + "${local.hostname}${count.index}" ) + + #groups = [ + # "role_${local.hostname}", + # var.prefix != "" ? "prj_${var.prefix}" : "" + # ] + vars = { + ansible_user = var.user + ansible_host = "${local.hostname}${count.index}.${var.domain}" + ansible_connection = "ssh" + + instances_count = var.number + instances_index = count.index + + instance_domain = var.domain + instance_hostname = "${local.hostname}${count.index}" + instance_name = "${local.hostname}" + + instance_provisionning_key = var.authorized_key + instance_provisionning_user = var.user + instance_flavor = var.flavor + + instance_metadata = jsonencode(merge(local.metadata, var.metadata)) + + # TOFIX: lol + instance_os_name = "debian" + instance_os_version = "10" + + instance_vm = ( var.name_fqdn ? + "${local.vmname}${count.index}.${var.domain}" : + "${local.vmname}${count.index}" ) + } +} + + +resource "libvirt_domain" "instdef" { + count = var.number + + autostart = true + name = ( var.name_fqdn ? + "${local.vmname}${count.index}.${var.domain}" : + "${local.vmname}${count.index}" ) + description = "${var.name}${count.index}.${var.domain}" + memory = var.memory + vcpu = var.vcpu + + metadata = jsonencode(merge(local.metadata, var.metadata)) + + # Root FS + # ------------------- + cloudinit = module.volume_cloudinit[count.index].volume.id + disk { + volume_id = module.volume_os[count.index].volume.id + } + + # Instance disks + dynamic "disk" { + for_each = module.volumes_extra[count.index].volumes + content { + volume_id = disk.value.volume.id + #volume_id = disk.value.info.id + #volume_id = coalesce(disk.value.volume.id) + #file = coalesce(disk.value.info.id) + } + } + + # Networking + # ------------------- + dynamic "network_interface" { + for_each = var.networks + content { + network_name = network_interface.value.name + mac = try(network_interface.value.mac, null) + addresses = try(network_interface.value.addresses, null) + #hostname = try(network_interface.value.hostname, var.name) + + wait_for_lease = try(network_interface.value.wait_for_lease, var.wait_for_lease ) + macvtap = try(network_interface.value.macvtap, null) + vepa = try(network_interface.value.vepa, null) + } + } + + # Other + # ------------------- + console { + type = "pty" + target_port = "0" + target_type = "serial" + } + + console { + type = "pty" + target_type = "virtio" + target_port = "1" + } + + graphics { + type = "spice" + listen_type = "address" + autoport = true + } + + cpu { + mode = "host-passthrough" + } + +} + + +# Outputs +# ===================== + +output "instance" { + value = try(resource.libvirt_domain.instdef, null) +} + + +# output "volume_cloudinit" { +# value = module.volume_cloudinit +# } +output "volume_os" { + value = module.volume_os +} +output "volumes_extra" { + value = module.volumes_extra +} + +# Tag infos +output "system_os" { + value = module.system_os +} +output "system_flavor" { + value = module.system_flavor +} + + + diff --git a/modules_libvirt/virt_instance_FULL_BACK/provider.tf b/modules_libvirt/virt_instance_FULL_BACK/provider.tf new file mode 100644 index 0000000..74925a4 --- /dev/null +++ b/modules_libvirt/virt_instance_FULL_BACK/provider.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + version = "0.6.14" + } + ansible = { + source = "nbering/ansible" + version = "~>1.0.4" + } + } +} diff --git a/modules_libvirt/virt_network/main.tf b/modules_libvirt/virt_network/main.tf new file mode 100644 index 0000000..3d5668b --- /dev/null +++ b/modules_libvirt/virt_network/main.tf @@ -0,0 +1,171 @@ + +# Variables +# ===================== + +variable "name" { + type = string + description = "Network name" +} + +variable "mode" { + description = "Network mode (route|nat)" + type = string + default = "nat" +} + +variable "bridge" { + description = "Network interface name (16 char max, virbr_)" + type = string +} + +variable "domain" { + description = "Network domain" + type = string + default = "local" +} + +variable "gw4" { + description = "Network address" + type = string + default = "192.168.0.1" +} + +variable "mask" { + description = "Netmask of the network" + type = number + default = 24 +} + + +# +variable "nat_enable" { + description = "Enable NAT from outside of libvirt" + type = bool + default = false +} + +variable "nat_driver" { + description = "Default NAT driver" + type = string + default = "ferm" +} + +variable "nat_device" { + description = "Device to forward unrouted traffic (ie: eth0)" + type = string + default = "enp1s0" +} + + +# +variable "vip" { + description = "List of vips" + type = list + default = [] +} + +variable "subnets" { + description = "List of subnets" + type = list + default = [] +} + + +# Resources +# ===================== + +resource "libvirt_network" "netdef" { + name = var.name + mode = var.mode + bridge = var.bridge + domain = var.domain + addresses = ["${var.gw4}/${var.mask}"] + autostart = true + + dns { + enabled = true + local_only = true + + hosts { + hostname = "gw" + ip = cidrhost("${var.gw4}/${var.mask}", 1) + } + + dynamic "hosts" { + for_each = var.vip + content { + hostname = try(hosts.value.hostname) + ip = try(hosts.value.ip) + } + } + } +} + + +# Tests +# ===================== + +# resource "consul_keys" "app" { +# count = var.nat_enable ? 1 : 0 +# +# depends_on = [libvirt_network.netdef] +# key { +# path = "tests/net/${var.bridge}/nat" +# value = "${var.nat_device} - ${ var.nat_enable }" +# delete = true +# } +# } + +# TMP DISABVLED, require ferm first resource "ssh_resource" "nat_config" { +# TMP DISABVLED, require ferm first count = var.nat_enable ? 1 : 0 +# TMP DISABVLED, require ferm first +# TMP DISABVLED, require ferm first host = "192.168.142.10" +# TMP DISABVLED, require ferm first user = "root" +# TMP DISABVLED, require ferm first agent = true +# TMP DISABVLED, require ferm first +# TMP DISABVLED, require ferm first depends_on = [libvirt_network.netdef] +# TMP DISABVLED, require ferm first +# TMP DISABVLED, require ferm first file { +# TMP DISABVLED, require ferm first content = templatefile("${path.module}/templates/ferm_nat.tpl", { +# TMP DISABVLED, require ferm first device = "${var.nat_device}" +# TMP DISABVLED, require ferm first driver = "${var.nat_driver}" +# TMP DISABVLED, require ferm first }) +# TMP DISABVLED, require ferm first destination = "/etc/libvirt/hooks/state/net/${var.bridge}_nat.ini" +# TMP DISABVLED, require ferm first permissions = "0644" +# TMP DISABVLED, require ferm first } +# TMP DISABVLED, require ferm first } + +# resource "ssh_resource" "ipvs_config" { +# count = var.nat_enable ? 1 : 0 +# +# host = "192.168.142.10" +# user = "root" +# agent = true +# +# depends_on = [libvirt_network.netdef] +# +# file { +# content = templatefile("${path.module}/templates/ipvs_config.tpl", { +# vips = var.vip +# }) +# destination = "/etc/libvirt/hooks/state/net/${var.bridge}_ipvs.ini" +# permissions = "0644" +# } +# } + + +# Output +# ===================== + +output "networks" { + value = resource.libvirt_network.netdef +} + +output "subnets" { + value = var.subnets +} + +output "vips" { + value = var.vip +} + diff --git a/modules_libvirt/virt_network/provider.tf b/modules_libvirt/virt_network/provider.tf new file mode 100644 index 0000000..7557e9b --- /dev/null +++ b/modules_libvirt/virt_network/provider.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + version = "0.6.14" + } + } +} diff --git a/modules_libvirt/virt_network/templates/ferm_nat.tpl b/modules_libvirt/virt_network/templates/ferm_nat.tpl new file mode 100644 index 0000000..678f181 --- /dev/null +++ b/modules_libvirt/virt_network/templates/ferm_nat.tpl @@ -0,0 +1,3 @@ +[nat] +driver = ${ driver } +device = ${ device } diff --git a/modules_libvirt/virt_network/templates/ipvs_config.tpl b/modules_libvirt/virt_network/templates/ipvs_config.tpl new file mode 100644 index 0000000..10fd21e --- /dev/null +++ b/modules_libvirt/virt_network/templates/ipvs_config.tpl @@ -0,0 +1,8 @@ +[ipvs] +%{ for vip in vips } +%{ if can(vip.public_ip) && can(vip.public_port) } +-A -t ${vip.public_ip}:${vip.public_port} -s rr +-a -t ${vip.public_ip}:${vip.public_port} -r ${vip.ip}:${try(vip.internal_port, vip.public_port)} -m -w 1 +%{ endif } +%{ endfor } + diff --git a/modules_libvirt/virt_pools/main.tf b/modules_libvirt/virt_pools/main.tf new file mode 100644 index 0000000..c930d4f --- /dev/null +++ b/modules_libvirt/virt_pools/main.tf @@ -0,0 +1,55 @@ + +# Variables +# ===================== + +variable "tenant" { + description = "Tenant informations" + type = any + default = { + prefix = "" + } +} + +variable "pools" { + description = "Pools definitions" + type = any + default = {} +} + +variable "pools_defaults" { + description = "Pools default definitions" + type = any + default = {} +} + + +locals { + defaults = { + root_dir = "/virt" + } + pools_defaults = merge(local.defaults, var.pools_defaults) +} + + +# Resources +# ===================== + +resource "libvirt_pool" "pooldef" { + for_each = var.pools + + name = "${each.key}" + path = length( + regexall("^/", try( each.value.dir, "")) + ) > 0 ? "${each.value.dir}" : "${local.pools_defaults.root_dir}/${try( each.value.dir, each.key)}" + type = "dir" + +} + + +# Output +# ===================== + +output "pools" { + value = resource.libvirt_pool.pooldef +} + diff --git a/modules_libvirt/virt_pools/provider.tf b/modules_libvirt/virt_pools/provider.tf new file mode 100644 index 0000000..7557e9b --- /dev/null +++ b/modules_libvirt/virt_pools/provider.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + version = "0.6.14" + } + } +} diff --git a/modules_libvirt/virt_volume/main.tf b/modules_libvirt/virt_volume/main.tf new file mode 100644 index 0000000..4e13583 --- /dev/null +++ b/modules_libvirt/virt_volume/main.tf @@ -0,0 +1,262 @@ + +# Variables +# ===================== +# +variable "name" { + type = string + description = "Determine the name of the volume" +} + +variable "persistant" { + type = bool + default = false + description = "Enable destroy protection on this volume" +} + + +# +variable "format" { + type = string + default = "qcow2" + description = "Determine the format of the image" +} + +variable "file" { + type = string + description = "Determine the filename of the volume" + default = "" +} + + +# +variable "pool" { + type = string + default = "default" + description = "Determine pool to create volume within" +} + +variable "pool_dir" { + type = string + default = "/virt" + description = "Default path for all pools" +} + +variable "size_gb" { + type = number + default = 0 + description = "Default size for the volume" +} + + +# +variable "url" { + type = string + default = "" + description = "Download url for volume" +} + +variable "base_pool" { + type = string + default = "default" + description = "Pool of the source image" +} +variable "base_file" { + type = string + description = "Name of the source image" + default = "" +} + + +# Variables +# ===================== + + + #agent=var.ssh_agent # "true" + #host=var.ssh_host # "wks1.office.barbu-it.net" + #user=var.ssh_user # "root" + #password=var.password + +variable "ssh_conn" { + description = "Default settings for ssh connexion" + type = any + default = { + agent = true, + #host = "localhost", + host = "wks1.office.barbu-it.net", + user = null, + password = null + } +} + + + +# Resources +# ===================== + +locals { + + # Guess things from URL + url_split = split(".", basename( var.url )) + url_ext = element(local.url_split, length(local.url_split ) -1 ) + url_file = "${var.name}.${local.url_ext}" + + # Guess things from name + name_file = "${var.name}.${var.format}" + + # Determine resource to use + is_baked = var.base_file != "" ? true : false + is_sourced = var.url != "" && ! local.is_baked ? true : false + is_created = ! local.is_baked && ! local.is_sourced ? true: false + + # Volume settings + vol_name = coalesce(var.file, local.is_sourced ? local.url_file : local.name_file) + size_gb = try(var.size_gb * 1024 * 1024 * 1024, null) + id = "${var.pool_dir}/${var.pool}/${local.vol_name}" + +} + + +# Sourced +resource "libvirt_volume" "voldef_ephemeral_sourced" { + count = local.is_sourced && ! var.persistant ? 1 : 0 + + name = local.vol_name + pool = var.pool + source = local.is_sourced ? var.url : null +} + +# Created +resource "libvirt_volume" "voldef_ephemeral_created" { + count = local.is_created && ! var.persistant ? 1 : 0 + + name = local.vol_name + pool = var.pool + source = local.is_sourced ? var.url : null + size = local.size_gb + format = var.format +} + +# Backed +resource "libvirt_volume" "voldef_ephemeral_baked" { + count = local.is_baked && ! var.persistant ? 1 : 0 + + name = local.vol_name + pool = var.pool + + size = local.is_baked ? try(local.size_gb, 1) : local.size_gb + base_volume_pool = local.is_baked ? var.base_pool : null + base_volume_name = local.is_baked ? var.base_file : null +} + +# Todo: Manage persistant volumes with: +# lifecycle { +# prevent_destroy = false +# } + +# # Created Persistant +# resource "libvirt_volume" "voldef_ephemeral_created" { +# +# name = local.vol_name +# pool = var.pool +# source = local.is_sourced ? var.url : null +# size = local.size_gb +# format = var.format +# } + + +resource "null_resource" "voldef_persistant_created" { + # We only support created volumes basically + count = var.persistant ? 1 : 0 + #count = local.is_created && var.persistant ? 1 : 0 + #count = 0 + + + # local ssh connection, make sure to pass variables 'user' andd 'password' on cli + # terraform apply -var user=$USER -var password=xxxxxxx + connection { + type="ssh" + # agent=var.ssh_agent # "true" + # host=var.ssh_host # "wks1.office.barbu-it.net" + # user=var.ssh_user # "root" + # password=var.password + + agent = var.ssh_conn.agent + host= var.ssh_conn.host + user = var.ssh_conn.user + password = var.ssh_conn.password + + #user=var.user + #password=var.password + + + } + + provisioner "remote-exec" { + inline = [ + "/opt/virt-scripts/bin/virt-vol.sh create ${local.vol_name} ${var.size_gb} ${var.pool} ${var.format}" + #"[[ ! -f '${/virt/cloud_safe/inst_storage0_data.qcow2}' ]] || \{ qemu-img create -f '${var.format}' ${local.id} '${local.size_gb}Gb; virsh pool-refresh '${var.pool}'; \}" + #"virsh vol-create-as --pool '${var.pool}' --name '${local.name_file}' --format '${var.format}' --prealloc-metadata --capacity '${local.size_gb * 100000000}'" + ] + } + + #provisioner "file" { + # destination = "/tmp/script.sh" + # content = templatefile( + # "${path.module}/script.sh.tpl", + # { + # "params": local.params + # "params_for_inline_command" : local.params_for_inline_command + # } + # ) + #} + +} + + + + +# Output +# ===================== + +output "volume" { + value = coalescelist( + resource.libvirt_volume.voldef_ephemeral_sourced, + resource.libvirt_volume.voldef_ephemeral_created, + resource.libvirt_volume.voldef_ephemeral_baked, + [ { id = local.id } ], + )[0] +} + +output "info" { + value = { + #id = local.id, + #id = coalescelist( + id = coalescelist( + resource.libvirt_volume.voldef_ephemeral_sourced, + resource.libvirt_volume.voldef_ephemeral_created, + resource.libvirt_volume.voldef_ephemeral_baked, + [ { id = local.id } ], + )[0].id + vol_name = local.vol_name, + is_baked = local.is_baked, + is_sourced = local.is_sourced, + is_created = local.is_created, + name_url = local.url_file, + name_file = local.name_file, + format = var.format, + file = var.file, + #out = [ + # resource.libvirt_volume.voldef_ephemeral_sourced, + # resource.libvirt_volume.voldef_ephemeral_baked, + # resource.libvirt_volume.voldef_ephemeral_created, + # resource.null_resource.voldef_persistant_created + #] + #volume_id = coalescelist( + # resource.libvirt_volume.voldef_ephemeral_sourced, + # resource.libvirt_volume.voldef_ephemeral_created, + # resource.libvirt_volume.voldef_ephemeral_baked, + # resource.null_resource.voldef_persistant_created, + # )[0].id + } +} + diff --git a/modules_libvirt/virt_volume/provider.tf b/modules_libvirt/virt_volume/provider.tf new file mode 100644 index 0000000..7557e9b --- /dev/null +++ b/modules_libvirt/virt_volume/provider.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + version = "0.6.14" + } + } +} diff --git a/modules_libvirt/virt_volumes/main.tf b/modules_libvirt/virt_volumes/main.tf new file mode 100644 index 0000000..51379a0 --- /dev/null +++ b/modules_libvirt/virt_volumes/main.tf @@ -0,0 +1,74 @@ + +# Variables +# ============ + +variable "tenant" { + description = "Tenant informations" + type = map + default = { + prefix = "" + } +} + +variable "volumes" { + description = "List of volumes" + type = list(any) + default = [] +} + +variable "volumes_defaults" { + description = "Default settings for volumes" + type = map + default = {} +} + + +# Main +# ============ + + +locals { + defaults = { + prefix = var.tenant.prefix + pool = "default", + pool_dir = "/virt", + size_gb = 10, + format = "qcow2" + persistant = false + + base_pool = "default", + base_file = "", + + url = "" + file = "" + } + volumes_defaults = merge(local.defaults, var.volumes_defaults) +} + + +module "cloud_volume" { + source = "../virt_volume" + for_each = { for v in var.volumes: v.name => merge(local.volumes_defaults, v) } + + name = "${each.value.prefix}${each.value.name}" + pool = each.value.pool + size_gb = each.value.size_gb + format = each.value.format + persistant = each.value.persistant + + base_pool = each.value.base_pool + base_file = each.value.base_file + + url = each.value.url + file = each.value.file + +} + + +# Output +# ============ + +output "volumes" { + value = module.cloud_volume +} + diff --git a/modules_libvirt/virt_volumes/provider.tf b/modules_libvirt/virt_volumes/provider.tf new file mode 100644 index 0000000..e69de29