diff --git a/roles/dtc/common/templates/ndfc_create_fabric.j2 b/roles/dtc/common/templates/ndfc_create_fabric.j2 index 41566296..715d3a91 100644 --- a/roles/dtc/common/templates/ndfc_create_fabric.j2 +++ b/roles/dtc/common/templates/ndfc_create_fabric.j2 @@ -1,3 +1,4 @@ +{% import 'ndfc_utils.j2' as ndfc_utils with context %} --- # This NDFC fabric config data structure is auto-generated # DO NOT EDIT MANUALLY @@ -19,79 +20,6 @@ RR_COUNT: {{ global.route_reflectors | default(defaults.vxlan.global.route_reflectors) }} ANYCAST_GW_MAC: {{ global.anycast_gateway_mac | default(defaults.vxlan.global.anycast_gateway_mac) }} {# ------------------------------------------------------ #} -{# Manageability Parameters #} -{# ------------------------------------------------------ #} -{% if global.dns_servers is defined and global.dns_servers %} -{% set dns_server_ips = [] %} -{% set dns_server_vrfs = [] %} -{% for dns in global.dns_servers %} -{% if dns.ip_address | string is defined and dns.ip_address %} -{% set dns_ip = dns.ip_address | string %} -{% set dns_server_ips = dns_server_ips.append(dns_ip) %} -{% endif %} -{% if dns.vrf | string is defined and dns.vrf %} -{% set dns_vrf = dns.vrf | string %} -{% set dns_server_vrfs = dns_server_vrfs.append(dns_vrf) %} -{% endif %} -{% endfor %} - DNS_SERVER_IP_LIST: {{ dns_server_ips | join(',') | default(omit) }} - DNS_SERVER_VRF: {{ dns_server_vrfs | join(',') | default(omit) }} -{% endif %} -{% if global.ntp_servers is defined and global.ntp_servers %} -{% set ntp_server_ips = [] %} -{% set ntp_server_vrfs = [] %} -{% for ntp in global.ntp_servers %} -{% if ntp.ip_address is defined and ntp.ip_address %} -{% set ntp_ip = ntp.ip_address | string %} -{% set ntp_server_ips = ntp_server_ips.append(ntp_ip) %} -{% endif %} -{% if ntp.vrf is defined and ntp.vrf %} -{% set ntp_vrf = ntp.vrf|string %} -{% set ntp_server_vrfs = ntp_server_vrfs.append(ntp_vrf) %} -{% endif %} -{% endfor %} - NTP_SERVER_IP_LIST: {{ ntp_server_ips | join(',') | default(omit) }} - NTP_SERVER_VRF: {{ ntp_server_vrfs | join(',') | default(omit) }} -{% endif %} -{% if global.syslog_servers is defined and global.syslog_servers %} -{% set syslog_server_ips = [] %} -{% set syslog_server_vrfs = [] %} -{% set syslog_server_sevs = [] %} -{% for syslog_server in global.syslog_servers %} -{% if syslog_server.ip_address is defined and syslog_server.ip_address %} -{% set syslog_server_ip = syslog_server.ip_address | string %} -{% set syslog_server_ips = syslog_server_ips.append(syslog_server_ip) %} -{% endif %} -{% if syslog_server.vrf is defined and syslog_server.vrf %} -{% set syslog_server_vrf = syslog_server.vrf | string %} -{% set syslog_server_vrfs = syslog_server_vrfs.append(syslog_server_vrf) %} -{% endif %} -{% if syslog_server.severity is defined and syslog_server.severity %} -{% set syslog_server_sev = syslog_server.severity %} -{% set syslog_server_sevs = syslog_server_sevs.append(syslog_server_sev) %} -{% endif %} -{% endfor %} - SYSLOG_SERVER_IP_LIST: {{ syslog_server_ips | join(',') | default(omit) }} -{% if syslog_server_ips is defined and syslog_server_ips %} - SYSLOG_SERVER_VRF: {{ syslog_server_vrfs | join(',') | default(omit) }} - SYSLOG_SEV: {{ syslog_server_sevs | join(',') | default(omit) }} -{% endif %} -{% endif %} -{# ------------------------------------------------------ #} -{# vPC Parameters #} -{# ------------------------------------------------------ #} - VPC_PEER_LINK_VLAN: {{ vxlan.global.vpc.peer_link_vlan | default(defaults.vxlan.global.vpc.peer_link_vlan) }} - VPC_PEER_KEEP_ALIVE_OPTION: {{ vxlan.global.vpc.peer_keep_alive | default(defaults.vxlan.global.vpc.peer_keep_alive) }} - VPC_AUTO_RECOVERY_TIME: {{ vxlan.global.vpc.auto_recovery_time | default(defaults.vxlan.global.vpc.auto_recovery_time) }} - VPC_DELAY_RESTORE_TIME: {{ vxlan.global.vpc.delay_restore_time | default(defaults.vxlan.global.vpc.delay_restore_time) }} - VPC_PEER_LINK_PO: {{ vxlan.global.vpc.peer_link_port_channel_id | default(defaults.vxlan.global.vpc.peer_link_port_channel_id) }} - VPC_DOMAIN_ID_RANGE: {{ vxlan.global.vpc.domain_id_range | default(defaults.vxlan.global.vpc.domain_id_range) }} - VPC_DELAY_RESTORE: {{ vxlan.global.vpc.delay_restore_time | default(defaults.vxlan.global.vpc.delay_restore_time) }} - ADVERTISE_PIP_BGP: {{ (vxlan.global.vpc.advertise_pip | default(defaults.vxlan.global.vpc.advertise_pip) | title) }} -{% if (vxlan.global.vpc.advertise_pip | default(defaults.vxlan.global.vpc.advertise_pip) | title) == 'False' %} - ADVERTISE_PIP_ON_BORDER: {{ vxlan.global.vpc.advertise_pip_border_only | default(defaults.vxlan.global.vpc.advertise_pip_border_only) }} -{% endif %} -{# ------------------------------------------------------ #} {# Underlay General Parameters #} {# ------------------------------------------------------ #} BGP_LB_ID: {{ underlay.general.underlay_routing_loopback_id | default(defaults.vxlan.underlay.general.underlay_routing_loopback_id) }} @@ -210,6 +138,91 @@ {% endif %} {% endif %} {# ------------------------------------------------------ #} +{# vPC Parameters #} +{# ------------------------------------------------------ #} + VPC_PEER_LINK_VLAN: {{ vpc.peer_link_vlan | default(defaults.vxlan.global.vpc.peer_link_vlan) }} + VPC_PEER_KEEP_ALIVE_OPTION: {{ vpc.peer_keep_alive | default(defaults.vxlan.global.vpc.peer_keep_alive) }} + VPC_AUTO_RECOVERY_TIME: {{ vpc.auto_recovery_time | default(defaults.vxlan.global.vpc.auto_recovery_time) }} + VPC_DELAY_RESTORE_TIME: {{ vpc.delay_restore_time | default(defaults.vxlan.global.vpc.delay_restore_time) }} + VPC_PEER_LINK_PO: {{ vpc.peer_link_port_channel_id | default(defaults.vxlan.global.vpc.peer_link_port_channel_id) }} + VPC_DOMAIN_ID_RANGE: {{ vpc.domain_id_range | default(defaults.vxlan.global.vpc.domain_id_range) }} + VPC_DELAY_RESTORE: {{ vpc.delay_restore_time | default(defaults.vxlan.global.vpc.delay_restore_time) }} + ADVERTISE_PIP_BGP: {{ (vpc.advertise_pip | default(defaults.vxlan.global.vpc.advertise_pip) | title) }} +{% if (vpc.advertise_pip | default(defaults.vxlan.global.vpc.advertise_pip) | title) == 'False' %} + ADVERTISE_PIP_ON_BORDER: {{ vpc.advertise_pip_border_only | default(defaults.vxlan.global.vpc.advertise_pip_border_only) }} +{% endif %} +{# ------------------------------------------------------ #} +{# STP Parameters #} +{# ------------------------------------------------------ #} +{# STP_ROOT_OPTION: {{ global.spanning_tree.root_bridge_protocol | default(defaults.vxlan.global.spanning_tree.root_bridge_protocol) }} +{% if global.spanning_tree.root_bridge_protocol is defined and global.spanning_tree.root_bridge_protocol != 'unmanaged' %} +{% if (global.spanning_tree.root_bridge_protocol | default(defaults.vxlan.global.spanning_tree.root_bridge_protocol)) == 'rpvst+' %} + STP_VLAN_RANGE: {{ ndfc_utils.convert_ranges(global.spanning_tree.vlan_range, defaults.vxlan.global.spanning_tree.vlan_range) }} +{% elif (global.spanning_tree.root_bridge_protocol | default(defaults.vxlan.global.spanning_tree.root_bridge_protocol)) == 'mst' %} + MST_INSTANCE_RANGE: {{ ndfc_utils.convert_ranges(global.spanning_tree.mst_instance_range, defaults.vxlan.global.spanning_tree.mst_instance_range) }} +{% endif %} + STP_BRIDGE_PRIORITY: {{ global.spanning_tree.bridge_priority | default(defaults.vxlan.global.spanning_tree.bridge_priority) }} +{% endif %}#} +{# ------------------------------------------------------ #} +{# Manageability Parameters #} +{# ------------------------------------------------------ #} +{% if global.dns_servers is defined and global.dns_servers %} +{% set dns_server_ips = [] %} +{% set dns_server_vrfs = [] %} +{% for dns in global.dns_servers %} +{% if dns.ip_address | string is defined and dns.ip_address %} +{% set dns_ip = dns.ip_address | string %} +{% set dns_server_ips = dns_server_ips.append(dns_ip) %} +{% endif %} +{% if dns.vrf | string is defined and dns.vrf %} +{% set dns_vrf = dns.vrf | string %} +{% set dns_server_vrfs = dns_server_vrfs.append(dns_vrf) %} +{% endif %} +{% endfor %} + DNS_SERVER_IP_LIST: {{ dns_server_ips | join(',') | default(omit) }} + DNS_SERVER_VRF: {{ dns_server_vrfs | join(',') | default(omit) }} +{% endif %} +{% if global.ntp_servers is defined and global.ntp_servers %} +{% set ntp_server_ips = [] %} +{% set ntp_server_vrfs = [] %} +{% for ntp in global.ntp_servers %} +{% if ntp.ip_address is defined and ntp.ip_address %} +{% set ntp_ip = ntp.ip_address | string %} +{% set ntp_server_ips = ntp_server_ips.append(ntp_ip) %} +{% endif %} +{% if ntp.vrf is defined and ntp.vrf %} +{% set ntp_vrf = ntp.vrf|string %} +{% set ntp_server_vrfs = ntp_server_vrfs.append(ntp_vrf) %} +{% endif %} +{% endfor %} + NTP_SERVER_IP_LIST: {{ ntp_server_ips | join(',') | default(omit) }} + NTP_SERVER_VRF: {{ ntp_server_vrfs | join(',') | default(omit) }} +{% endif %} +{% if global.syslog_servers is defined and global.syslog_servers %} +{% set syslog_server_ips = [] %} +{% set syslog_server_vrfs = [] %} +{% set syslog_server_sevs = [] %} +{% for syslog_server in global.syslog_servers %} +{% if syslog_server.ip_address is defined and syslog_server.ip_address %} +{% set syslog_server_ip = syslog_server.ip_address | string %} +{% set syslog_server_ips = syslog_server_ips.append(syslog_server_ip) %} +{% endif %} +{% if syslog_server.vrf is defined and syslog_server.vrf %} +{% set syslog_server_vrf = syslog_server.vrf | string %} +{% set syslog_server_vrfs = syslog_server_vrfs.append(syslog_server_vrf) %} +{% endif %} +{% if syslog_server.severity is defined and syslog_server.severity %} +{% set syslog_server_sev = syslog_server.severity %} +{% set syslog_server_sevs = syslog_server_sevs.append(syslog_server_sev) %} +{% endif %} +{% endfor %} + SYSLOG_SERVER_IP_LIST: {{ syslog_server_ips | join(',') | default(omit) }} +{% if syslog_server_ips is defined and syslog_server_ips %} + SYSLOG_SERVER_VRF: {{ syslog_server_vrfs | join(',') | default(omit) }} + SYSLOG_SEV: {{ syslog_server_sevs | join(',') | default(omit) }} +{% endif %} +{% endif %} +{# ------------------------------------------------------ #} {# Flow Monitor Parameters #} {# ------------------------------------------------------ #} ENABLE_NETFLOW: {{ global.netflow.enable | default(defaults.vxlan.global.netflow.enable) }} diff --git a/roles/dtc/common/templates/ndfc_interface_trunk.j2 b/roles/dtc/common/templates/ndfc_interface_trunk.j2 index a4068020..a2fb1a54 100644 --- a/roles/dtc/common/templates/ndfc_interface_trunk.j2 +++ b/roles/dtc/common/templates/ndfc_interface_trunk.j2 @@ -27,7 +27,7 @@ speed: {{ interface['speed'] | default(defaults.vxlan.topology.switches.interfaces.topology_switch_trunk_interface.speed) }} port_type_fast: {{ interface['spanning_tree_portfast'] | default(defaults.vxlan.topology.switches.interfaces.topology_switch_trunk_interface.spanning_tree_portfast) }} bpdu_guard: {{ interface['enable_bpdu_guard'] | default(defaults.vxlan.topology.switches.interfaces.topology_switch_trunk_interface.enable_bpdu_guard) }} - allowed_vlans: "{{ ndfc_utils.convert_vlan_ranges(interface['trunk_allowed_vlans'], defaults.vxlan.topology.switches.interfaces.topology_switch_trunk_interface.trunk_allowed_vlans) | trim }}" + allowed_vlans: "{{ ndfc_utils.convert_ranges(interface['trunk_allowed_vlans'], defaults.vxlan.topology.switches.interfaces.topology_switch_trunk_interface.trunk_allowed_vlans) | trim }}" {% endif %} {% endif %} {% endfor %} diff --git a/roles/dtc/common/templates/ndfc_interface_trunk_po.j2 b/roles/dtc/common/templates/ndfc_interface_trunk_po.j2 index c7f6aa89..bd4bcad7 100644 --- a/roles/dtc/common/templates/ndfc_interface_trunk_po.j2 +++ b/roles/dtc/common/templates/ndfc_interface_trunk_po.j2 @@ -30,7 +30,7 @@ pc_mode: {{ interface['pc_mode'] | default(defaults.vxlan.topology.switches.interfaces.topology_switch_access_po_interface.pc_mode) }} members: {{ interface['members'] | default(omit) }} bpdu_guard: {{ interface['enable_bpdu_guard'] | default(defaults.vxlan.topology.switches.interfaces.topology_switch_trunk_po_interface.enable_bpdu_guard) }} - allowed_vlans: "{{ ndfc_utils.convert_vlan_ranges(interface['trunk_allowed_vlans'], defaults.vxlan.topology.switches.interfaces.topology_switch_trunk_po_interface.trunk_allowed_vlans) | trim }}" + allowed_vlans: "{{ ndfc_utils.convert_ranges(interface['trunk_allowed_vlans'], defaults.vxlan.topology.switches.interfaces.topology_switch_trunk_po_interface.trunk_allowed_vlans) | trim }}" {% endif %} {% endif %} {% endif %} diff --git a/roles/dtc/common/templates/ndfc_utils.j2 b/roles/dtc/common/templates/ndfc_utils.j2 index ba4542c3..53f27961 100644 --- a/roles/dtc/common/templates/ndfc_utils.j2 +++ b/roles/dtc/common/templates/ndfc_utils.j2 @@ -1,18 +1,18 @@ -{# Macro: convert_vlan_ranges +{# Macro: convert_ranges - This macro takes a list of VLAN ranges and converts it into a string representation. + This macro takes a list of ranges and converts it into a string representation. Each item in the list should be a dictionary with 'from' and optionally 'to' keys. If 'to' is provided, it represents a range from 'from' to 'to'. - If 'to' is not provided, it represents a single VLAN. + If 'to' is not provided, it represents a single primitive. - If the allowed VLANs are not defined, the macro will return value defined - by the 'default_vlan_range' parameter passed from the parent template. + If the allowed ranges are not defined, the macro will return value defined + by the 'default_range' parameter passed from the parent template. The macro can be called from the parent template as follows: {% import 'ndfc_utils.j2' as vlan_macro with context %} ... - {{ ndfc_utils.convert_vlan_ranges( variable, parameter) | trim }} + {{ ndfc_utils.convert_ranges( variable, parameter) | trim }} **NOTE: trim is currently required when calling the macro to remove empty space VLAN ranges transformation example: @@ -33,19 +33,19 @@ "none" #} -{% macro convert_vlan_ranges(trunk_allowed_vlans, default_vlan_range) %} - {% if trunk_allowed_vlans is defined %} - {% set vlan_ranges = [] %} - {% for vlan_range in trunk_allowed_vlans %} - {% if vlan_range.to is defined %} - {% set range_str = vlan_range.from ~ ('-' ~ vlan_range.to) %} - {% else %} - {% set range_str = vlan_range.from %} - {% endif %} - {% set _ = vlan_ranges.append(range_str) %} - {% endfor %} - {{ vlan_ranges | join(',') }} - {% elif trunk_allowed_vlans is not defined %} - {{ default_vlan_range }} - {% endif %} -{% endmacro %} +{%- macro convert_ranges(allowed_ranges, default_range) -%} + {%- if allowed_ranges is defined -%} + {%- set ranges = [] -%} + {%- for range in allowed_ranges -%} + {%- if range.to is defined -%} + {%- set range_str = range.from ~ ('-' ~ range.to) -%} + {%- else -%} + {%- set range_str = range.from -%} + {%- endif -%} + {%- set _ = ranges.append(range_str) -%} + {%- endfor -%} + {{ ranges | join(',') }} + {%- elif allowed_ranges is not defined -%} + {{ default_range }} + {%- endif -%} +{%- endmacro -%} diff --git a/roles/dtc/create/tasks/sub_main.yml b/roles/dtc/create/tasks/sub_main.yml index cb1ec174..e6a4012d 100644 --- a/roles/dtc/create/tasks/sub_main.yml +++ b/roles/dtc/create/tasks/sub_main.yml @@ -73,3 +73,5 @@ - (MD_Extended.vxlan.policy is defined) and (MD_Extended.vxlan.policy.policies | length > 0) - changes_detected_policy tags: "{{ nac_tags.create_policy }}" + +- ansible.builtin.meta: end_play diff --git a/roles/validate/files/defaults.yml b/roles/validate/files/defaults.yml index 4472c867..063255ae 100644 --- a/roles/validate/files/defaults.yml +++ b/roles/validate/files/defaults.yml @@ -35,6 +35,15 @@ factory_defaults: advertise_pip: false advertise_pip_border_only: true domain_id_range: 1-1000 + spanning_tree: + root_bridge_protocol: unmanaged + vlan_range: + - from: 1 + to: 3967 + mst_instance_range: + - from: 0 + to: 0 + bridge_priority: 0 netflow: enable: false topology: diff --git a/roles/validate/files/rules/required_rules/201_global_spanning_tree.py b/roles/validate/files/rules/required_rules/201_global_spanning_tree.py new file mode 100644 index 00000000..4ddcf811 --- /dev/null +++ b/roles/validate/files/rules/required_rules/201_global_spanning_tree.py @@ -0,0 +1,23 @@ +class Rule: + id = "201" + description = "Verify a spanning tree protocol mutually exclusive parameters." + severity = "HIGH" + + @classmethod + def match(cls, inventory): + results = [] + if inventory.get("vxlan", None): + if inventory["vxlan"].get("global", None): + if inventory["vxlan"].get("global", None).get("spanning_tree", None): + root_bridge_protocol = inventory["vxlan"]["global"]["spanning_tree"].get("root_bridge_protocol", None) + vlan_range = inventory["vxlan"]["global"]["spanning_tree"].get("vlan_range", None) + mst_instance_range = inventory["vxlan"]["global"]["spanning_tree"].get("mst_instance_range", None) + + if vlan_range and mst_instance_range: + results.append( + "vxlan.global.spanning_tree.vlan_range and vxlan.global.spanning_tree.mst_instance_range " + "both cannot be configured at the same time. Please choose one depending on the " + "vxlan.global.spanning_tree.root_bridge_protocol selected." + ) + + return results