diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index deb3a607..e4d7c3ef 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -481,6 +481,11 @@ repos: args: ["./modules/terraform-aci-ptp"] - id: terraform-docs-system args: ["./modules/terraform-aci-ptp/examples/complete"] + - id: terraform-docs-system + args: ["./modules/terraform-aci-ptp-profile"] + - id: terraform-docs-system + args: ["./modules/terraform-aci-ptp-profile/examples/complete"] + - id: terraform-docs-system - id: terraform-docs-system args: ["./modules/terraform-aci-qos"] - id: terraform-docs-system diff --git a/README.md b/README.md index e4d17f9a..e7238a78 100644 --- a/README.md +++ b/README.md @@ -273,6 +273,7 @@ Additional example repositories: | [aci\_port\_tracking](#module\_aci\_port\_tracking) | ./modules/terraform-aci-port-tracking | n/a | | [aci\_psu\_policy](#module\_aci\_psu\_policy) | ./modules/terraform-aci-psu-policy | n/a | | [aci\_ptp](#module\_aci\_ptp) | ./modules/terraform-aci-ptp | n/a | +| [aci\_ptp\_profile](#module\_aci\_ptp\_profile) | ./modules/terraform-aci-ptp-profile | n/a | | [aci\_qos](#module\_aci\_qos) | ./modules/terraform-aci-qos | n/a | | [aci\_qos\_policy](#module\_aci\_qos\_policy) | ./modules/terraform-aci-qos-policy | n/a | | [aci\_radius](#module\_aci\_radius) | ./modules/terraform-aci-radius | n/a | diff --git a/aci_access_policies.tf b/aci_access_policies.tf index 40173001..c5f978eb 100644 --- a/aci_access_policies.tf +++ b/aci_access_policies.tf @@ -819,3 +819,18 @@ module "aci_vspan_session" { }] }] } + +module "aci_ptp_profile" { + source = "./modules/terraform-aci-ptp-profile" + + for_each = { for profile in try(local.access_policies.ptp_profiles, []) : profile.name => profile if local.modules.aci_ptp_profile && var.manage_access_policies } + name = each.value.name + announce_interval = try(each.value.announce_interval, local.defaults.apic.access_policies.ptp_profiles.announce_interval) + announce_timeout = try(each.value.announce_timeout, local.defaults.apic.access_policies.ptp_profiles.announce_timeout) + delay_interval = try(each.value.delay_interval, local.defaults.apic.access_policies.ptp_profiles.delay_interval) + forwardable = try(each.value.forwardable, local.defaults.apic.access_policies.ptp_profiles.forwardable) + priority = try(each.value.priority, local.defaults.apic.access_policies.ptp_profiles.priority) + sync_interval = try(each.value.sync_interval, local.defaults.apic.access_policies.ptp_profiles.sync_interval) + template = try(each.value.template, local.defaults.apic.access_policies.ptp_profiles.template) + mismatch_handling = try(each.value.mismatch_handling, local.defaults.apic.access_policies.ptp_profiles.mismatch_handling) +} \ No newline at end of file diff --git a/aci_tenants.tf b/aci_tenants.tf index 0ee9a594..afaadb6a 100644 --- a/aci_tenants.tf +++ b/aci_tenants.tf @@ -363,6 +363,9 @@ locals { vlan = try(sp.vlan, null) deployment_immediacy = try(sp.deployment_immediacy, local.defaults.apic.tenants.application_profiles.endpoint_groups.static_ports.deployment_immediacy) mode = try(sp.mode, local.defaults.apic.tenants.application_profiles.endpoint_groups.static_ports.mode) + ptp_source_ip = try(sp.ptp.source_ip, local.defaults.apic.tenants.application_profiles.endpoint_groups.static_ports.ptp.source_ip) + ptp_mode = try(sp.ptp.mode, local.defaults.apic.tenants.application_profiles.endpoint_groups.static_ports.ptp.mode) + ptp_profile = try(sp.ptp.profile, null) }] static_leafs = [for sl in try(epg.static_leafs, []) : { pod_id = try(sl.pod_id, null) @@ -442,6 +445,9 @@ module "aci_endpoint_group" { vlan = sp.vlan deployment_immediacy = sp.deployment_immediacy mode = sp.mode + ptp_source_ip = sp.ptp_source_ip + ptp_mode = sp.ptp_mode + ptp_profile = sp.ptp_profile }] static_leafs = [for sl in try(each.value.static_leafs, []) : { pod_id = sl.pod_id == null ? try([for node in try(local.node_policies.nodes, []) : node.pod if node.id == sl.node_id][0], local.defaults.apic.node_policies.nodes.pod) : sl.pod_id diff --git a/defaults/defaults.yaml b/defaults/defaults.yaml index bd51c632..38d9e9cd 100644 --- a/defaults/defaults.yaml +++ b/defaults/defaults.yaml @@ -681,6 +681,15 @@ defaults: direction: both access_paths: module: 1 + ptp_profiles: + announce_interval: 1 + announce_timeout: 3 + delay_interval: -3 + sync_interval: -3 + priority: 128 + template: aes67 + forwardable: true + mismatch_handling: configured node_policies: oob_endpoint_group: default inb_endpoint_group: default diff --git a/defaults/modules.yaml b/defaults/modules.yaml index 146b71a0..171c5152 100644 --- a/defaults/modules.yaml +++ b/defaults/modules.yaml @@ -115,6 +115,7 @@ modules: aci_port_tracking: true aci_psu_policy: true aci_ptp: true + aci_ptp_profile: true aci_qos: true aci_qos_policy: true aci_radius: true diff --git a/modules/terraform-aci-endpoint-group/README.md b/modules/terraform-aci-endpoint-group/README.md index 67293a45..f7c75098 100644 --- a/modules/terraform-aci-endpoint-group/README.md +++ b/modules/terraform-aci-endpoint-group/README.md @@ -69,6 +69,9 @@ module "aci_endpoint_group" { module = 1 deployment_immediacy = "lazy" mode = "untagged" + ptp = { + profile = "PTP1" + } }] static_leafs = [{ node_id = 102 @@ -142,7 +145,7 @@ module "aci_endpoint_group" { | [subnets](#input\_subnets) | List of subnets. Default value `public`: `false`. Default value `shared`: `false`. Default value `igmp_querier`: `false`. Default value `nd_ra_prefix`: `true`. Default value `no_default_gateway`: `false`. `nlb_mode` allowed values: `mode-mcast-igmp`, `mode-uc` or `mode-mcast-static`. |
list(object({
description = optional(string, "")
ip = string
public = optional(bool, false)
shared = optional(bool, false)
igmp_querier = optional(bool, false)
nd_ra_prefix = optional(bool, true)
no_default_gateway = optional(bool, false)
nd_ra_prefix_policy = optional(string, "")
ip_pools = optional(list(object({
name = string
start_ip = optional(string, "0.0.0.0")
end_ip = optional(string, "0.0.0.0")
dns_search_suffix = optional(string, "")
dns_server = optional(string, "")
dns_suffix = optional(string, "")
wins_server = optional(string, "")
})), [])
next_hop_ip = optional(string, "")
anycast_mac = optional(string, "")
nlb_group = optional(string, "0.0.0.0")
nlb_mac = optional(string, "00:00:00:00:00:00")
nlb_mode = optional(string, "")
}))
| `[]` | no | | [vmware\_vmm\_domains](#input\_vmware\_vmm\_domains) | List of VMware VMM domains. Default value `u_segmentation`: `false`. Default value `netflow`: `false`. Choices `deployment_immediacy`: `immediate`, `lazy`. Default value `deployment_immediacy`: `lazy`. Choices `resolution_immediacy`: `immediate`, `lazy`, `pre-provision`. Default value `resolution_immediacy`: `immediate`. Default value `allow_promiscuous`: `false`. Default value `forged_transmits`: `false`. Default value `mac_changes`: `false`. |
list(object({
name = string
u_segmentation = optional(bool, false)
delimiter = optional(string, "")
vlan = optional(number)
primary_vlan = optional(number)
secondary_vlan = optional(number)
netflow = optional(bool, false)
deployment_immediacy = optional(string, "lazy")
resolution_immediacy = optional(string, "immediate")
allow_promiscuous = optional(bool, false)
forged_transmits = optional(bool, false)
mac_changes = optional(bool, false)
custom_epg_name = optional(string, "")
elag = optional(string, "")
active_uplinks_order = optional(string, "")
standby_uplinks = optional(string, "")
}))
| `[]` | no | | [static\_leafs](#input\_static\_leafs) | List of static leaf switches. Allowed values `pod_id`: `1` - `255`. Default value `pod_id`: `1`. Allowed values `node_id`: `1` - `4000`. Allowed values `vlan`: `1` - `4096`. Choices `mode`: `regular`, `native`, `untagged`. Default value `mode`: `regular`. Choices `deployment_immediacy`: `immediate`, `lazy`. Default value `deployment_immediacy`: `immediate` |
list(object({
pod_id = optional(number, 1)
node_id = number
vlan = number
mode = optional(string, "regular")
deployment_immediacy = optional(string, "immediate")
}))
| `[]` | no | -| [static\_ports](#input\_static\_ports) | List of static ports. Allowed values `node_id`, `node2_id`: `1` - `4000`. Allowed values `fex_id`, `fex2_id`: `101` - `199`. Allowed values `vlan`: `1` - `4096`. Allowed values `pod_id`: `1` - `255`. Default value `pod_id`: `1`. Allowed values `port`: `1` - `127`. Allowed values `sub_port`: `1` - `16`. Allowed values `module`: `1` - `9`. Default value `module`: `1`. Choices `deployment_immediacy`: `immediate`, `lazy`. Default value `deployment_immediacy`: `lazy`. Choices `mode`: `regular`, `native`, `untagged`. Default value `mode`: `regular`. |
list(object({
node_id = number
node2_id = optional(number)
fex_id = optional(number)
fex2_id = optional(number)
vlan = number
pod_id = optional(number, 1)
port = optional(number)
sub_port = optional(number)
module = optional(number, 1)
channel = optional(string)
deployment_immediacy = optional(string, "lazy")
mode = optional(string, "regular")
}))
| `[]` | no | +| [static\_ports](#input\_static\_ports) | List of static ports. Allowed values `node_id`, `node2_id`: `1` - `4000`. Allowed values `fex_id`, `fex2_id`: `101` - `199`. Allowed values `vlan`: `1` - `4096`. Allowed values `pod_id`: `1` - `255`. Default value `pod_id`: `1`. Allowed values `port`: `1` - `127`. Allowed values `sub_port`: `1` - `16`. Allowed values `module`: `1` - `9`. Default value `module`: `1`. Choices `deployment_immediacy`: `immediate`, `lazy`. Default value `deployment_immediacy`: `lazy`. Choices `mode`: `regular`, `native`, `untagged`. Default value `mode`: `regular`. |
list(object({
node_id = number
node2_id = optional(number)
fex_id = optional(number)
fex2_id = optional(number)
vlan = number
pod_id = optional(number, 1)
port = optional(number)
sub_port = optional(number)
module = optional(number, 1)
channel = optional(string)
deployment_immediacy = optional(string, "lazy")
mode = optional(string, "regular")
ptp_source_ip = optional(string, "0.0.0.0")
ptp_mode = optional(string, "multicast")
ptp_profile = optional(string)
}))
| `[]` | no | | [static\_endpoints](#input\_static\_endpoints) | List of static endpoints. Format `mac`: `12:34:56:78:9A:BC`. Choices `type`: `silent-host`, `tep`, `vep`. Allowed values `node_id`, `node2_id`: `1` - `4000`. Allowed values `vlan`: `1` - `4096`. Allowed values `pod_id`: `1` - `255`. Default value `pod_id`: `1`. Allowed values `port`: `1` - `127`. Allowed values `module`: `1` - `9`. Default value `module`: `1`. |
list(object({
name = optional(string, "")
alias = optional(string, "")
mac = string
ip = optional(string, "0.0.0.0")
type = string
node_id = optional(number)
node2_id = optional(number)
vlan = optional(number)
pod_id = optional(number, 1)
port = optional(number)
module = optional(number, 1)
channel = optional(string)
additional_ips = optional(list(string), [])
}))
| `[]` | no | | [l4l7\_virtual\_ips](#input\_l4l7\_virtual\_ips) | List of EPG L4/L7 Virtual IPs. |
list(object({
ip = string
description = optional(string, "")
}))
| `[]` | no | | [l4l7\_address\_pools](#input\_l4l7\_address\_pools) | List of EPG L4/L7 Address Pools. |
list(object({
name = string
gateway_address = string
from = optional(string, "")
to = optional(string, "")
}))
| `[]` | no | @@ -191,6 +194,11 @@ module "aci_endpoint_group" { | [aci_rest_managed.fvVip](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | | [aci_rest_managed.fvnsUcastAddrBlk](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | | [aci_rest_managed.ipNexthopEpP](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | +| [aci_rest_managed.ptpEpgCfg_channel](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | +| [aci_rest_managed.ptpEpgCfg_fex_channel](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | +| [aci_rest_managed.ptpEpgCfg_fex_port](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | +| [aci_rest_managed.ptpEpgCfg_port](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | +| [aci_rest_managed.ptpEpgCfg_subport](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | | [aci_rest_managed.tagInst](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | | [aci_rest_managed.vmmSecP](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | | [aci_rest_managed.vnsAddrInst](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource | diff --git a/modules/terraform-aci-endpoint-group/examples/complete/README.md b/modules/terraform-aci-endpoint-group/examples/complete/README.md index 44770730..70e383b8 100644 --- a/modules/terraform-aci-endpoint-group/examples/complete/README.md +++ b/modules/terraform-aci-endpoint-group/examples/complete/README.md @@ -72,6 +72,9 @@ module "aci_endpoint_group" { module = 1 deployment_immediacy = "lazy" mode = "untagged" + ptp = { + profile = "PTP1" + } }] static_leafs = [{ node_id = 102 diff --git a/modules/terraform-aci-endpoint-group/examples/complete/main.tf b/modules/terraform-aci-endpoint-group/examples/complete/main.tf index c882101d..1bfbb121 100644 --- a/modules/terraform-aci-endpoint-group/examples/complete/main.tf +++ b/modules/terraform-aci-endpoint-group/examples/complete/main.tf @@ -58,6 +58,9 @@ module "aci_endpoint_group" { module = 1 deployment_immediacy = "lazy" mode = "untagged" + ptp = { + profile = "PTP1" + } }] static_leafs = [{ node_id = 102 diff --git a/modules/terraform-aci-endpoint-group/main.tf b/modules/terraform-aci-endpoint-group/main.tf index fb97785c..5da9ae6b 100644 --- a/modules/terraform-aci-endpoint-group/main.tf +++ b/modules/terraform-aci-endpoint-group/main.tf @@ -224,6 +224,23 @@ resource "aci_rest_managed" "fvRsPathAtt_port" { } } +resource "aci_rest_managed" "ptpEpgCfg_port" { + for_each = { for sp in var.static_ports : (sp.module != 1 ? "${sp.node_id}-${sp.module}-${sp.port}-vl-${sp.vlan}" : "${sp.node_id}-${sp.port}-vl-${sp.vlan}") => sp if sp.ptp_profile != null && sp.channel == null && sp.fex_id == null && sp.sub_port == null } + dn = "${aci_rest_managed.fvRsPathAtt_port[each.key].dn}/ptpEpgCfg" + class_name = "ptpEpgCfg" + content = { + srcIp = each.value.ptp_source_ip + ptpMode = each.value.ptp_mode + } + child { + class_name = "ptpRsProfile" + rn = "rsprofile" + content = { + "tDn" = "uni/infra/ptpprofile-${each.value.ptp_profile}" + } + } +} + resource "aci_rest_managed" "fvRsPathAtt_subport" { for_each = { for sp in var.static_ports : (sp.module != 1 ? "${sp.node_id}-${sp.module}-${sp.port}-${sp.sub_port}-vl-${sp.vlan}" : "${sp.node_id}-${sp.port}-${sp.sub_port}-vl-${sp.vlan}") => sp if sp.channel == null && sp.fex_id == null && sp.sub_port != null } dn = "${aci_rest_managed.fvAEPg.dn}/rspathAtt-[${format("topology/pod-%s/paths-%s/pathep-[eth%s/%s/%s]", each.value.pod_id, each.value.node_id, each.value.module, each.value.port, each.value.sub_port)}]" @@ -236,6 +253,23 @@ resource "aci_rest_managed" "fvRsPathAtt_subport" { } } +resource "aci_rest_managed" "ptpEpgCfg_subport" { + for_each = { for sp in var.static_ports : (sp.module != 1 ? "${sp.node_id}-${sp.module}-${sp.port}-${sp.sub_port}-vl-${sp.vlan}" : "${sp.node_id}-${sp.port}-${sp.sub_port}-vl-${sp.vlan}") => sp if sp.ptp_profile != null && sp.channel == null && sp.fex_id == null && sp.sub_port != null } + dn = "${aci_rest_managed.ptpEpgCfg_subport[each.key].dn}/ptpEpgCfg" + class_name = "ptpEpgCfg" + content = { + srcIp = each.value.ptp_source_ip + ptpMode = each.value.ptp_mode + } + child { + class_name = "ptpRsProfile" + rn = "rsprofile" + content = { + "tDn" = "uni/infra/ptpprofile-${each.value.ptp_profile}" + } + } +} + resource "aci_rest_managed" "fvRsPathAtt_channel" { for_each = { for sp in var.static_ports : "${sp.node_id}-${sp.channel}-vl-${sp.vlan}" => sp if sp.channel != null && sp.fex_id == null } dn = "${aci_rest_managed.fvAEPg.dn}/rspathAtt-[${format(each.value.node2_id != null ? "topology/pod-%s/protpaths-%s-%s/pathep-[%s]" : "topology/pod-%s/paths-%s/pathep-[%[4]s]", each.value.pod_id, each.value.node_id, each.value.node2_id, each.value.channel)}]" @@ -248,6 +282,23 @@ resource "aci_rest_managed" "fvRsPathAtt_channel" { } } +resource "aci_rest_managed" "ptpEpgCfg_channel" { + for_each = { for sp in var.static_ports : "${sp.node_id}-${sp.channel}-vl-${sp.vlan}" => sp if sp.ptp_profile != null && sp.channel != null && sp.fex_id == null } + dn = "${aci_rest_managed.ptpEpgCfg_channel[each.key].dn}/ptpEpgCfg" + class_name = "ptpEpgCfg" + content = { + srcIp = each.value.ptp_source_ip + ptpMode = each.value.ptp_mode + } + child { + class_name = "ptpRsProfile" + rn = "rsprofile" + content = { + "tDn" = "uni/infra/ptpprofile-${each.value.ptp_profile}" + } + } +} + resource "aci_rest_managed" "fvRsPathAtt_fex_port" { for_each = { for sp in var.static_ports : (sp.module != 1 ? "${sp.node_id}-${sp.fex_id}-${sp.module}-${sp.port}-vl-${sp.vlan}" : "${sp.node_id}-${sp.fex_id}-${sp.port}-vl-${sp.vlan}") => sp if sp.channel == null && sp.fex_id != null } dn = "${aci_rest_managed.fvAEPg.dn}/rspathAtt-[${format("topology/pod-%s/paths-%s/extpaths-%s/pathep-[eth%s/%s]", each.value.pod_id, each.value.node_id, each.value.fex_id, each.value.module, each.value.port)}]" @@ -260,6 +311,23 @@ resource "aci_rest_managed" "fvRsPathAtt_fex_port" { } } +resource "aci_rest_managed" "ptpEpgCfg_fex_port" { + for_each = { for sp in var.static_ports : (sp.module != 1 ? "${sp.node_id}-${sp.fex_id}-${sp.module}-${sp.port}-vl-${sp.vlan}" : "${sp.node_id}-${sp.fex_id}-${sp.port}-vl-${sp.vlan}") => sp if sp.ptp_profile != null && sp.channel == null && sp.fex_id != null } + dn = "${aci_rest_managed.ptpEpgCfg_fex_port[each.key].dn}/ptpEpgCfg" + class_name = "ptpEpgCfg" + content = { + srcIp = each.value.ptp_source_ip + ptpMode = each.value.ptp_mode + } + child { + class_name = "ptpRsProfile" + rn = "rsprofile" + content = { + "tDn" = "uni/infra/ptpprofile-${each.value.ptp_profile}" + } + } +} + resource "aci_rest_managed" "fvRsPathAtt_fex_channel" { for_each = { for sp in var.static_ports : "${sp.node_id}-${sp.fex_id}-${sp.channel}-vl-${sp.vlan}" => sp if sp.channel != null && sp.fex_id != null } dn = "${aci_rest_managed.fvAEPg.dn}/rspathAtt-[${format(each.value.node2_id != null && each.value.fex2_id != null ? "topology/pod-%s/protpaths-%s-%s/extprotpaths-%s-%s/pathep-[%s]" : "topology/pod-%s/paths-%s/extpaths-%[4]s/pathep-[%[6]s]", each.value.pod_id, each.value.node_id, each.value.node2_id, each.value.fex_id, each.value.fex2_id, each.value.channel)}]" @@ -272,6 +340,23 @@ resource "aci_rest_managed" "fvRsPathAtt_fex_channel" { } } +resource "aci_rest_managed" "ptpEpgCfg_fex_channel" { + for_each = { for sp in var.static_ports : "${sp.node_id}-${sp.fex_id}-${sp.channel}-vl-${sp.vlan}" => sp if sp.ptp_profile != null && sp.channel != null && sp.fex_id != null } + dn = "${aci_rest_managed.ptpEpgCfg_fex_channel[each.key].dn}/ptpEpgCfg" + class_name = "ptpEpgCfg" + content = { + srcIp = each.value.ptp_source_ip + ptpMode = each.value.ptp_mode + } + child { + class_name = "ptpRsProfile" + rn = "rsprofile" + content = { + "tDn" = "uni/infra/ptpprofile-${each.value.ptp_profile}" + } + } +} + resource "aci_rest_managed" "fvStCEp" { for_each = { for sp_ep in var.static_endpoints : sp_ep.name => sp_ep } dn = "${aci_rest_managed.fvAEPg.dn}/stcep-${each.value.mac}-type-${each.value.type}" diff --git a/modules/terraform-aci-endpoint-group/variables.tf b/modules/terraform-aci-endpoint-group/variables.tf index 16a7eda5..104214e3 100644 --- a/modules/terraform-aci-endpoint-group/variables.tf +++ b/modules/terraform-aci-endpoint-group/variables.tf @@ -411,9 +411,32 @@ variable "static_ports" { channel = optional(string) deployment_immediacy = optional(string, "lazy") mode = optional(string, "regular") + ptp_source_ip = optional(string, "0.0.0.0") + ptp_mode = optional(string, "multicast") + ptp_profile = optional(string) })) default = [] + validation { + condition = alltrue([ + for sp in var.static_ports : can(regex("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", sp.ptp_source_ip)) + ]) + error_message = "`ptp_source_ip` is not a valid IPv4 address." + } + + validation { + condition = alltrue([ + for sp in var.static_ports : sp.ptp == null || try(contains(["multicast", "multicast-master", "unicast-master"], sp.ptp_mode), false) + ]) + error_message = "`ptp_mode`: Allowed values are `multicast`, `multicast-master` or `unicast-master`." + } + + validation { + condition = alltrue([ + for sp in var.static_ports : can(regex("^[a-zA-Z0-9_.:-]{0,16}$", sp.ptp_profile)) + ]) + error_message = "`ptp_profile`: Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `:`, `-`. Maximum characters: 16." + } validation { condition = alltrue([ diff --git a/modules/terraform-aci-ptp-profile/.terraform-docs.yml b/modules/terraform-aci-ptp-profile/.terraform-docs.yml new file mode 100644 index 00000000..d5505d04 --- /dev/null +++ b/modules/terraform-aci-ptp-profile/.terraform-docs.yml @@ -0,0 +1,34 @@ +version: '>= 0.14.0' + +formatter: markdown table + +content: |- + # Terraform ACI PTP Profile Module + + Manages ACI PTP Profile + + Location in GUI: + `Fabric` » `Access Policies` » `Policies` » `Global` » `PTP User Profile` + + ## Examples + + ```hcl + {{ include "./examples/complete/main.tf" }} + ``` + + {{ .Requirements }} + + {{ .Providers }} + + {{ .Inputs }} + + {{ .Outputs }} + + {{ .Resources }} + +output: + file: README.md + mode: replace + +sort: + enabled: false diff --git a/modules/terraform-aci-ptp-profile/README.md b/modules/terraform-aci-ptp-profile/README.md new file mode 100644 index 00000000..45b140c1 --- /dev/null +++ b/modules/terraform-aci-ptp-profile/README.md @@ -0,0 +1,67 @@ + +# Terraform ACI PTP Profile Module + +Manages ACI PTP Profile + +Location in GUI: +`Fabric` » `Access Policies` » `Policies` » `Global` » `PTP User Profile` + +## Examples + +```hcl +module "aci_ptp_profile" { + source = "netascode/nac-aci/aci//modules/terraform-aci-aaep" + version = ">= 0.8.0" + + name = "PTP1" + announce_interval = -3 + announce_timeout = 2 + delay_interval = -4 + sync_interval = -4 + forwardable = false + template = "telecom" + mismatch_handling = "configured" + priority = 201 +} +``` + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3.0 | +| [aci](#requirement\_aci) | >= 2.0.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aci](#provider\_aci) | >= 2.0.0 | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [name](#input\_name) | PTP Profile name. | `string` | n/a | yes | +| [announce\_interval](#input\_announce\_interval) | Announcement Interval. Minimum value: -3. Maximum value: 4. | `number` | `1` | no | +| [announce\_timeout](#input\_announce\_timeout) | Announcement Timeout. Minimum value: 2. Maximum value: 10. | `number` | `3` | no | +| [delay\_interval](#input\_delay\_interval) | Delay Interval. Minimum value: -4. Maximum value: 5 | `number` | `-3` | no | +| [forwardable](#input\_forwardable) | Destination MAC of PTP Messages | `bool` | `true` | no | +| [priority](#input\_priority) | Teracom G.8275.1 Profile Priority. Minimum value: 1. Maximum value: 255. | `number` | `128` | no | +| [sync\_interval](#input\_sync\_interval) | Sync Interval. Minimum value: -4. Maximum value: 1. | `number` | `1` | no | +| [template](#input\_template) | Profile Template. | `string` | `"aes67"` | no | +| [mismatch\_handling](#input\_mismatch\_handling) | Mismatched Destination MAC Handling. | `string` | `"configured"` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [dn](#output\_dn) | Distinguished name of `ptpProfile` object. | +| [name](#output\_name) | PTP Profile Name. | + +## Resources + +| Name | Type | +|------|------| +| [aci_rest_managed.ptpProfile](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-ptp-profile/examples/complete/.terraform-docs.yml b/modules/terraform-aci-ptp-profile/examples/complete/.terraform-docs.yml new file mode 100644 index 00000000..5b94588c --- /dev/null +++ b/modules/terraform-aci-ptp-profile/examples/complete/.terraform-docs.yml @@ -0,0 +1,24 @@ +version: '>= 0.14.0' + +formatter: markdown table + +content: |- + # PTP Profile Example + + To run this example you need to execute: + + ```bash + $ terraform init + $ terraform plan + $ terraform apply + ``` + + Note that this example will create resources. Resources can be destroyed with `terraform destroy`. + + ```hcl + {{ include "./main.tf" }} + ``` + +output: + file: README.md + mode: replace diff --git a/modules/terraform-aci-ptp-profile/examples/complete/README.md b/modules/terraform-aci-ptp-profile/examples/complete/README.md new file mode 100644 index 00000000..df9d6fb5 --- /dev/null +++ b/modules/terraform-aci-ptp-profile/examples/complete/README.md @@ -0,0 +1,30 @@ + +# PTP Profile Example + +To run this example you need to execute: + +```bash +$ terraform init +$ terraform plan +$ terraform apply +``` + +Note that this example will create resources. Resources can be destroyed with `terraform destroy`. + +```hcl +module "aci_ptp_profile" { + source = "netascode/nac-aci/aci//modules/terraform-aci-aaep" + version = ">= 0.8.0" + + name = "PTP1" + announce_interval = -3 + announce_timeout = 2 + delay_interval = -4 + sync_interval = -4 + forwardable = false + template = "telecom" + mismatch_handling = "configured" + priority = 201 +} +``` + \ No newline at end of file diff --git a/modules/terraform-aci-ptp-profile/examples/complete/main.tf b/modules/terraform-aci-ptp-profile/examples/complete/main.tf new file mode 100644 index 00000000..3ace5d89 --- /dev/null +++ b/modules/terraform-aci-ptp-profile/examples/complete/main.tf @@ -0,0 +1,14 @@ +module "aci_ptp_profile" { + source = "netascode/nac-aci/aci//modules/terraform-aci-aaep" + version = ">= 0.8.0" + + name = "PTP1" + announce_interval = -3 + announce_timeout = 2 + delay_interval = -4 + sync_interval = -4 + forwardable = false + template = "telecom" + mismatch_handling = "configured" + priority = 201 +} diff --git a/modules/terraform-aci-ptp-profile/examples/complete/versions.tf b/modules/terraform-aci-ptp-profile/examples/complete/versions.tf new file mode 100644 index 00000000..9299fb61 --- /dev/null +++ b/modules/terraform-aci-ptp-profile/examples/complete/versions.tf @@ -0,0 +1,11 @@ + +terraform { + required_version = ">= 1.3.0" + + required_providers { + aci = { + source = "CiscoDevNet/aci" + version = ">= 2.0.0" + } + } +} diff --git a/modules/terraform-aci-ptp-profile/main.tf b/modules/terraform-aci-ptp-profile/main.tf new file mode 100644 index 00000000..a0e35b8c --- /dev/null +++ b/modules/terraform-aci-ptp-profile/main.tf @@ -0,0 +1,15 @@ +resource "aci_rest_managed" "ptpProfile" { + dn = "uni/infra/ptpprofile-${var.name}" + class_name = "ptpProfile" + content = { + name = var.name + announceIntvl = var.announce_interval + announceTimeout = var.announce_timeout + delayIntvl = var.delay_interval + profileTemplate = var.template == "telecom" ? "telecom_full_path" : var.template == "smpte" ? "smpte" : "aes67" + ptpoeDstMacRxNoMatch = var.mismatch_handling == "configured" ? "replyWithCfgMac" : var.mismatch_handling == "received" ? "replyWithRxMac" : "drop" + ptpoeDstMacType = var.forwardable ? "forwardable" : "non-forwardable" + syncIntvl = var.sync_interval + localPriority = var.priority + } +} \ No newline at end of file diff --git a/modules/terraform-aci-ptp-profile/outputs.tf b/modules/terraform-aci-ptp-profile/outputs.tf new file mode 100644 index 00000000..0886238a --- /dev/null +++ b/modules/terraform-aci-ptp-profile/outputs.tf @@ -0,0 +1,9 @@ +output "dn" { + value = aci_rest_managed.ptpProfile.id + description = "Distinguished name of `ptpProfile` object." +} + +output "name" { + value = aci_rest_managed.ptpProfile.content.name + description = "PTP Profile Name." +} diff --git a/modules/terraform-aci-ptp-profile/variables.tf b/modules/terraform-aci-ptp-profile/variables.tf new file mode 100644 index 00000000..00cf6107 --- /dev/null +++ b/modules/terraform-aci-ptp-profile/variables.tf @@ -0,0 +1,92 @@ +variable "name" { + description = "PTP Profile name." + type = string + + validation { + condition = can(regex("^[a-zA-Z0-9_.:-]{0,16}$", var.name)) + error_message = "Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `:`, `-`. Maximum characters: 64." + } +} + +variable "announce_interval" { + description = "Announcement Interval. Minimum value: -3. Maximum value: 4." + type = number + default = 1 + + validation { + condition = var.announce_interval >= -3 && var.announce_interval <= 4 + error_message = "Minimum value: -3. Maximum value: 4." + } +} + +variable "announce_timeout" { + description = "Announcement Timeout. Minimum value: 2. Maximum value: 10." + type = number + default = 3 + + validation { + condition = var.announce_timeout >= 2 && var.announce_timeout <= 10 + error_message = "Minimum value: 2. Maximum value: 10." + } +} + +variable "delay_interval" { + description = "Delay Interval. Minimum value: -4. Maximum value: 5" + type = number + default = -3 + + validation { + condition = var.delay_interval >= -4 && var.delay_interval <= 5 + error_message = "Minimum value: -4. Maximum value: 5." + } +} + +variable "forwardable" { + description = "Destination MAC of PTP Messages" + default = true + type = bool +} + +variable "priority" { + description = "Teracom G.8275.1 Profile Priority. Minimum value: 1. Maximum value: 255." + type = number + default = 128 + + validation { + condition = var.priority >= 1 && var.priority <= 255 + error_message = "Minimum value: 1. Maximum value: 255." + } +} + +variable "sync_interval" { + description = "Sync Interval. Minimum value: -4. Maximum value: 1." + type = number + default = 1 + + validation { + condition = var.sync_interval >= -4 && var.sync_interval <= 1 + error_message = "Minimum value: -4. Maximum value: 1." + } +} + +variable "template" { + description = "Profile Template." + type = string + default = "aes67" + + validation { + condition = can(contains(["aes67", "smpte", "telecom"], var.template)) + error_message = "Allowed values: `aes67`, `smpte` or `telecom`." + } +} + +variable "mismatch_handling" { + description = "Mismatched Destination MAC Handling." + type = string + default = "configured" + + validation { + condition = can(contains(["drop", "configured", "received"], var.mismatch_handling)) + error_message = "Allowed values: `drop`, `configured` or `received`." + } +} \ No newline at end of file diff --git a/modules/terraform-aci-ptp-profile/versions.tf b/modules/terraform-aci-ptp-profile/versions.tf new file mode 100644 index 00000000..9299fb61 --- /dev/null +++ b/modules/terraform-aci-ptp-profile/versions.tf @@ -0,0 +1,11 @@ + +terraform { + required_version = ">= 1.3.0" + + required_providers { + aci = { + source = "CiscoDevNet/aci" + version = ">= 2.0.0" + } + } +}