diff --git a/CHANGELOG.md b/CHANGELOG.md index da92e81a..8c4803a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.2 (unreleased) + +- Add support for PBR L1L2 destinations + ## 0.8.1 - Make L3 PBR destination MAC optional diff --git a/aci_tenants.tf b/aci_tenants.tf index 7e9e8f8b..6d0ac6ea 100644 --- a/aci_tenants.tf +++ b/aci_tenants.tf @@ -2848,6 +2848,17 @@ locals { pod_id = try(dest.pod, local.defaults.apic.tenants.services.redirect_policies.l3_destinations.pod) redirect_health_group = try("${dest.redirect_health_group}${local.defaults.apic.tenants.services.redirect_health_groups.name_suffix}", "") }] + l1l2_destinations = [for dest in try(policy.l1l2_destinations, []) : { + description = try(dest.description, "") + name = dest.name + mac = try(dest.mac, null) + weight = try(dest.weight, null) + pod_id = try(dest.pod, null) + redirect_health_group = try("${dest.redirect_health_group}${local.defaults.apic.tenants.services.redirect_health_groups.name_suffix}", "") + l4l7_device = "${dest.concrete_interface.l4l7_device}${local.defaults.apic.tenants.services.l4l7_devices.name_suffix}" + concrete_device = "${dest.concrete_interface.concrete_device}${local.defaults.apic.tenants.services.l4l7_devices.concrete_devices.name_suffix}" + interface = "${dest.concrete_interface.interface}${local.defaults.apic.tenants.services.l4l7_devices.concrete_devices.interfaces.name_suffix}" + }] } ] ]) @@ -2873,6 +2884,7 @@ module "aci_redirect_policy" { ip_sla_policy = each.value.ip_sla_policy redirect_backup_policy = each.value.redirect_backup_policy l3_destinations = each.value.l3_destinations + l1l2_destinations = each.value.l1l2_destinations depends_on = [ module.aci_tenant, diff --git a/modules/terraform-aci-redirect-policy/README.md b/modules/terraform-aci-redirect-policy/README.md index 73df3096..e5664e34 100644 --- a/modules/terraform-aci-redirect-policy/README.md +++ b/modules/terraform-aci-redirect-policy/README.md @@ -72,6 +72,7 @@ module "aci_redirect_policy" { | [ip\_sla\_policy](#input\_ip\_sla\_policy) | IP SLA Policy Name. | `string` | `""` | no | | [redirect\_backup\_policy](#input\_redirect\_backup\_policy) | Redirect Backup Policy Name. | `string` | `""` | no | | [l3\_destinations](#input\_l3\_destinations) | List of L3 destinations. Allowed values `pod`: 1-255. |
list(object({| `[]` | no | +| [l1l2\_destinations](#input\_l1l2\_destinations) | List of L1L2 destinations. |
description = optional(string, "")
ip = string
ip_2 = optional(string)
mac = optional(string)
pod_id = optional(number, 1)
redirect_health_group = optional(string, "")
}))
list(object({| `[]` | no | ## Outputs @@ -84,9 +85,12 @@ module "aci_redirect_policy" { | Name | Type | |------|------| +| [aci_rest_managed.vnsL1L2RedirectDest](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | | [aci_rest_managed.vnsRedirectDest](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | | [aci_rest_managed.vnsRsBackupPol](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | | [aci_rest_managed.vnsRsIPSLAMonitoringPol](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | +| [aci_rest_managed.vnsRsL1L2RedirectHealthGroup](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | | [aci_rest_managed.vnsRsRedirectHealthGroup](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | +| [aci_rest_managed.vnsRsToCIf](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | | [aci_rest_managed.vnsSvcRedirectPol](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | \ No newline at end of file diff --git a/modules/terraform-aci-redirect-policy/main.tf b/modules/terraform-aci-redirect-policy/main.tf index 9a54e760..84616b6d 100644 --- a/modules/terraform-aci-redirect-policy/main.tf +++ b/modules/terraform-aci-redirect-policy/main.tf @@ -60,3 +60,34 @@ resource "aci_rest_managed" "vnsRsRedirectHealthGroup" { "tDn" = "uni/tn-${var.tenant}/svcCont/redirectHealthGroup-${each.value.redirect_health_group}" } } + +resource "aci_rest_managed" "vnsL1L2RedirectDest" { + for_each = { for destination in var.l1l2_destinations : destination.name => destination } + dn = "${aci_rest_managed.vnsSvcRedirectPol.dn}/L1L2RedirectDest-${each.value.name}" + class_name = "vnsL1L2RedirectDest" + content = { + descr = each.value.description + destName = each.value.name + mac = each.value.mac + weight = each.value.weight + podId = each.value.pod_id + } +} + +resource "aci_rest_managed" "vnsRsL1L2RedirectHealthGroup" { + for_each = { for destination in var.l1l2_destinations : destination.name => destination if destination.redirect_health_group != "" } + dn = "${aci_rest_managed.vnsL1L2RedirectDest[each.value.name].dn}/rsL1L2RedirectHealthGroup" + class_name = "vnsRsL1L2RedirectHealthGroup" + content = { + "tDn" = "uni/tn-${var.tenant}/svcCont/redirectHealthGroup-${each.value.redirect_health_group}" + } +} + +resource "aci_rest_managed" "vnsRsToCIf" { + for_each = { for destination in var.l1l2_destinations : destination.name => destination } + dn = "${aci_rest_managed.vnsL1L2RedirectDest[each.value.name].dn}/rstoCIf" + class_name = "vnsRsToCIf" + content = { + "tDn" = "uni/tn-${var.tenant}/lDevVip-${each.value.l4l7_device}/cDev-${each.value.concrete_device}/cIf-[${each.value.interface}]" + } +} diff --git a/modules/terraform-aci-redirect-policy/variables.tf b/modules/terraform-aci-redirect-policy/variables.tf index 70a231b8..93ff49d3 100644 --- a/modules/terraform-aci-redirect-policy/variables.tf +++ b/modules/terraform-aci-redirect-policy/variables.tf @@ -173,3 +173,81 @@ variable "l3_destinations" { } } +variable "l1l2_destinations" { + description = "List of L1L2 destinations." + type = list(object({ + description = optional(string, "") + name = string + mac = optional(string) + weight = optional(number) + pod_id = optional(number) + redirect_health_group = optional(string, "") + l4l7_device = string + concrete_device = string + interface = string + })) + default = [] + + validation { + condition = alltrue([ + for l1l2 in var.l1l2_destinations : l1l2.name == null || can(regex("^[a-zA-Z0-9_.-]{0,64}$", l1l2.name)) + ]) + error_message = "`name`: Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `-`. Maximum characters: 64." + } + + validation { + condition = alltrue([ + for l1l2 in var.l1l2_destinations : l1l2.description == null || can(regex("^[a-zA-Z0-9\\!#$%()*,-./:;@ _{|}~?&+]{0,128}$", l1l2.description)) + ]) + error_message = "`description`: Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `\\`, `!`, `#`, `$`, `%`, `(`, `)`, `*`, `,`, `-`, `.`, `/`, `:`, `;`, `@`, ` `, `_`, `{`, `|`, }`, `~`, `?`, `&`, `+`. Maximum characters: 128." + } + + validation { + condition = alltrue([ + for l1l2 in var.l1l2_destinations : l1l2.pod_id == null || try(l1l2.pod_id >= 1 && l1l2.pod_id <= 255, false) + ]) + error_message = "`pod_id`: Minimum value: 1. Maximum value: 255." + } + + validation { + condition = alltrue([ + for l1l2 in var.l1l2_destinations : l1l2.mac == null || (l1l2.mac != "" && l1l2.mac != "00:00:00:00:00:00") + ]) + error_message = "`mac`: Empty string or `00:00:00:00:00:00` are not allowed." + } + + validation { + condition = alltrue([ + for l1l2 in var.l1l2_destinations : l1l2.weight == null || try(l1l2.weight >= 1 && l1l2.weight <= 10, false) + ]) + error_message = "`weight`: Minimum value: 1. Maximum value: 10." + } + + validation { + condition = alltrue([ + for l1l2 in var.l1l2_destinations : can(regex("^[a-zA-Z0-9_.-]{0,64}$", l1l2.redirect_health_group)) + ]) + error_message = "`redirect_health_group` allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `-`. Maximum characters: 64." + } + + validation { + condition = alltrue([ + for l1l2 in var.l1l2_destinations : l1l2.l4l7_device == null || can(regex("^[a-zA-Z0-9_.-]{0,64}$", l1l2.l4l7_device)) + ]) + error_message = "`l4l7_device`: Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `-`. Maximum characters: 64." + } + + validation { + condition = alltrue([ + for l1l2 in var.l1l2_destinations : l1l2.concrete_device == null || can(regex("^[a-zA-Z0-9_.-]{0,64}$", l1l2.concrete_device)) + ]) + error_message = "`concrete_device`: Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `-`. Maximum characters: 64." + } + + validation { + condition = alltrue([ + for l1l2 in var.l1l2_destinations : l1l2.interface == null || can(regex("^[a-zA-Z0-9_.-]{0,64}$", l1l2.interface)) + ]) + error_message = "`interface`: Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `-`. Maximum characters: 64." + } +}
description = optional(string, "")
name = string
mac = optional(string)
weight = optional(number)
pod_id = optional(number)
redirect_health_group = optional(string, "")
l4l7_device = string
concrete_device = string
interface = string
}))