Skip to content

Commit

Permalink
Add Fabric STP (#164)
Browse files Browse the repository at this point in the history
* add fabric stp

* refactor convert range macro

* remove f-strings

* fix lint errors

* update defaults

* update to rule & temp update to template

* remove unneeded rule checks

* add initial version compare filter

* more work on versioning and splitting templates

* fix if clause & add placeholders

* address review comments

* remove backup file

* address pipeline errors

* fix vpc model path
  • Loading branch information
mtarking authored Nov 4, 2024
1 parent c49d8d1 commit 54866e4
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 97 deletions.
159 changes: 86 additions & 73 deletions roles/dtc/common/templates/ndfc_create_fabric.j2
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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) }}
Expand Down Expand Up @@ -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) }}
Expand Down
2 changes: 1 addition & 1 deletion roles/dtc/common/templates/ndfc_interface_trunk.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}
Expand Down
2 changes: 1 addition & 1 deletion roles/dtc/common/templates/ndfc_interface_trunk_po.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}
Expand Down
44 changes: 22 additions & 22 deletions roles/dtc/common/templates/ndfc_utils.j2
Original file line number Diff line number Diff line change
@@ -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(<trunk_allowed_vlans> variable, <default_vlan_range> parameter) | trim }}
{{ ndfc_utils.convert_ranges(<allowed_ranges> variable, <default_range> parameter) | trim }}
**NOTE: trim is currently required when calling the macro to remove empty space
VLAN ranges transformation example:
Expand All @@ -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 -%}
2 changes: 2 additions & 0 deletions roles/dtc/create/tasks/sub_main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions roles/validate/files/defaults.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 54866e4

Please sign in to comment.