Skip to content

update for manual underlay ipv6 #552

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 54 additions & 6 deletions roles/dtc/common/templates/ndfc_underlay_ip_address.j2
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,21 @@
{%- endfor %}
{% macro generate_loopback_config(loopback_id) %}
{%- for switch in vxlan.topology.switches %}
{%- set ns = namespace(ipv4="") %}
{%- set ns = namespace(ip="") %}
{%- for interface in switch.interfaces %}
{%- if interface.name.lower() == "loopback" ~ loopback_id or interface.name.lower() == "lo" ~ loopback_id %}
{%- set ns.ipv4 = interface.ipv4_address %}
{%- if interface.ipv4_address is defined and interface.ipv4_address is iterable %}
{%- set ns.ip = interface.ipv4_address %}
{%- elif interface.ipv6_address is defined and interface.ipv6_address is iterable %}
{%- set ns.ip = interface.ipv6_address %}
{%- endif %}
{%- endif %}
{%- endfor %}
- entity_name: "{{ switch_list[switch.name].serial_number }}~loopback{{ loopback_id }}"
pool_type: IP
pool_name: "LOOPBACK{{ loopback_id }}_IP_POOL"
scope_type: device_interface
resource: "{{ ns.ipv4 }}"
resource: "{{ ns.ip }}"
switch:
- "{{ switch_list[switch.name].management_ipv4_address }}"
{% endfor %}
Expand All @@ -48,12 +52,25 @@
- "{{ switch_list[peer.peer1].management_ipv4_address }}"
{% endfor %}
{% endif %}
{# Configure IPv6 router_id #}
{% for switch in vxlan.topology.switches %}
{% if switch.manual_ipv6_router_id is defined and switch.manual_ipv6_router_id is iterable %}
- entity_name: "{{ switch_list[switch.name].serial_number }}"
pool_type: IP
pool_name: "ROUTER_ID_POOL"
scope_type: device
resource: "{{ switch.manual_ipv6_router_id }}"
switch:
- "{{ switch_list[switch.name].management_ipv4_address }}"
{% endif %}
{% endfor%}

{# build p2p links - Check if ipv4 or ipv6 is present to distinct with fabric_link with template #}
{# build subnet #}

{% if vxlan.topology.fabric_links is defined %}
{% for switch in vxlan.topology.fabric_links %}
{% if switch.ipv4 is iterable or switch.ipv6 is iterable %}
{# Allocation for IPv4 #}
{% if switch.ipv4 is defined and switch.ipv4 is iterable %}
- entity_name: "{{ switch_list[switch.source_device].serial_number }}~{{ switch.source_interface }}~{{ switch_list[switch.dest_device].serial_number }}~{{ switch.dest_interface }}"
pool_type: SUBNET
pool_name: "SUBNET"
Expand Down Expand Up @@ -81,7 +98,38 @@
- "{{ switch_list[switch.dest_device].management_ipv4_address }}"
{% endif %}
{% endif %}
{% endfor %}

{# Allocation for IPv6 #}
{% if switch.ipv6 is defined and switch.ipv6 is iterable %}
- entity_name: "{{ switch_list[switch.source_device].serial_number }}~{{ switch.source_interface }}~{{
switch_list[switch.dest_device].serial_number }}~{{ switch.dest_interface }}"
pool_type: SUBNET
pool_name: "SUBNET"
scope_type: link
resource: "{{ switch.ipv6.subnet }}"
switch:
- "{{ switch_list[switch.source_device].management_ipv4_address }}"

{# assign ips #}
{% if switch.ipv6.source_ipv6 is defined and switch.ipv6.dest_ipv6 is defined %}
- entity_name: "{{ switch_list[switch.source_device].serial_number }}~{{ switch.source_interface }}"
pool_type: IP
pool_name: "{{switch.ipv6.subnet}}"
scope_type: device_interface
resource: "{{ switch.ipv6.source_ipv6 }}"
switch:
- "{{ switch_list[switch.source_device].management_ipv4_address }}"

- entity_name: "{{ switch_list[switch.dest_device].serial_number }}~{{ switch.dest_interface }}"
pool_type: IP
pool_name: "{{switch.ipv6.subnet}}"
scope_type: device_interface
resource: "{{ switch.ipv6.dest_ipv6 }}"
switch:
- "{{ switch_list[switch.dest_device].management_ipv4_address }}"
{% endif %}
{% endif %}

{% endfor %}
{% endif %}
{% endif %}
12 changes: 10 additions & 2 deletions roles/dtc/create/tasks/common/fabric.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
skip_validation: "{{ True if vxlan.fabric.type == 'ISN' else omit }}"
config: "{{ vars_common_local.fabric_config }}"

- name: Create ANYCAST_RP in Nexus Dashboard

- name: Set and Create ANYCAST_RP in Nexus Dashboard
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets keep the same: Create ANYCAST_RP in Nexus Dashboard

cisco.dcnm.dcnm_resource_manager:
state: merged
fabric: "{{ vxlan.fabric.name }}"
Expand All @@ -60,7 +61,14 @@
pool_type: "IP"
pool_name: "ANYCAST_RP_IP_POOL"
scope_type: "fabric"
resource: "{{ vxlan.underlay.multicast.ipv4.anycast_rp }}"
resource: >-
{{
vxlan.underlay.multicast.ipv6.anycast_rp
if vxlan.underlay.general.enable_ipv6_underlay | default(false)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets use the shipped defaults variable here

else vxlan.underlay.multicast.ipv4.anycast_rp
}}
when:
- vxlan.underlay.general.manual_underlay_allocation is defined
- vxlan.underlay.general.manual_underlay_allocation
- vxlan.underlay.general.replication_mode is defined
- vxlan.underlay.general.replication_mode == "multicast"
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ def match(cls, inventory):
return cls.results
# Check if manual_underlay_allocation is set to true
general = cls.safeget(inventory, ['vxlan', 'underlay', 'general'])
if "manual_underlay_allocation" in general:
if general.get("enable_ipv6_underlay"):
underlay_af = 6
else:
underlay_af = 4
if "manual_underlay_allocation" in general and underlay_af == 4:
# Check if anycast_rp is configured:
# Check if anycast_rp is configured
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate lines

check = cls.data_model_key_check(inventory, ['vxlan', 'underlay', 'multicast', 'ipv4'])
if 'ipv4' not in check['keys_data']:
Expand Down Expand Up @@ -51,23 +56,50 @@ def match(cls, inventory):
return cls.results
interfaces = switch.get("interfaces")

# Check for Loopback{underlay_routing_loopback_id}
routing_loopback_name = f"loopback{underlay_routing_loopback_id}"
routing_loopback_found = cls.check_interface_with_ipv4(interfaces, routing_loopback_name)
vtep_loopback_name = f"loopback{underlay_vtep_loopback_id}"

if not routing_loopback_found:
cls.results.append(
f"Switch '{switch_name}' is missing a configured interface '{routing_loopback_name}' with an IPv4 address."
)
if underlay_af == 4:
routing_loopback_found = cls.check_interface_with_ipv4(interfaces, routing_loopback_name)
vtep_loopback_found = cls.check_interface_with_ipv4(interfaces, vtep_loopback_name)
if not routing_loopback_found:
cls.results.append(
f"Switch '{switch_name}' is missing a configured interface '{routing_loopback_name}' with an IPv{underlay_af} address."
)
if not vtep_loopback_found:
cls.results.append(
f"Switch '{switch_name}' is missing a configured interface '{vtep_loopback_name}' with an IPv{underlay_af} address."
)

# Check for Loopback{underlay_vtep_loopback_id}
vtep_loopback_name = f"loopback{underlay_vtep_loopback_id}"
vtep_loopback_found = cls.check_interface_with_ipv4(interfaces, vtep_loopback_name)
if underlay_af == 6:
routing_loopback_found = cls.check_interface_with_ipv6(interfaces, routing_loopback_name)
vtep_loopback_found = cls.check_interface_with_ipv6(interfaces, vtep_loopback_name)
if not routing_loopback_found:
cls.results.append(
f"Switch '{switch_name}' is missing a configured interface '{routing_loopback_name}' with an IPv{underlay_af} address."
)
if not vtep_loopback_found:
cls.results.append(
f"Switch '{switch_name}' is missing a configured interface '{vtep_loopback_name}' with an IPv{underlay_af} address."
)
if switch.get('manual_ipv6_router_id', None) is None:
cls.results.append(
f"Switch '{switch_name}' is missing a configured switches.manual_ipv6_router_id' with an IPv{underlay_af} address."
)

if not vtep_loopback_found:
cls.results.append(
f"Switch '{switch_name}' is missing a configured interface '{vtep_loopback_name}' with an IPv4 address."
)
check = cls.data_model_key_check(inventory, ["vxlan", "topology", "fabric_links"])
interface_numbering_v4 = cls.safeget(inventory, ["vxlan", "underlay", "ipv4"])
interface_numbering_v6 = cls.safeget(inventory, ["vxlan", "underlay", "ipv6"])
if (
'fabric_links' not in check['keys_data']
and (
(interface_numbering_v4 and interface_numbering_v4.get("fabric_interface_numbering") == "p2p")
or (interface_numbering_v6 and interface_numbering_v6.get("enable_ipv6_link_local_address") is False)
)
):
cls.results.append(
"Fabric Links is not configured, but P2P subnet is expected in this configuration."
)

# Check if vtep_ip exist in vpc_peers
cls.validate_vpc_peers_and_vtep_vip(inventory)
Expand Down Expand Up @@ -111,11 +143,22 @@ def validate_vpc_peers_and_vtep_vip(cls, inventory):
if peer.get("fabric_peering") is False or interface_numbering["fabric_interface_numbering"] == "p2p":
cls.validate_fabric_links(inventory, vpc_peers_list)

check = cls.data_model_key_check(inventory, ["vxlan", "underlay", "ipv6"])
if 'ipv6' in check['keys_data']:
interface_numbering = cls.safeget(inventory, ["vxlan", "underlay", "ipv6"])
# Check IP address under vxlan.topology.fabric_link only if
# fabric numbering is global or fabric peering is false (Use Fabric Peer-Link)
if peer.get("fabric_peering") is False or interface_numbering["enable_ipv6_link_local_address"] is False:
cls.validate_fabric_links(inventory, vpc_peers_list)

@classmethod
def validate_fabric_links(cls, inventory, vpc_peers_list):
"""
Validates fabric links to ensure that IPv4 configuration is present for vPC peer connections.
Validates fabric links to ensure that IP configuration is present for vPC peer connections.
"""
interface_numbering_v4 = cls.safeget(inventory, ["vxlan", "underlay", "ipv4"])
interface_numbering_v6 = cls.safeget(inventory, ["vxlan", "underlay", "ipv6"])

check = cls.data_model_key_check(inventory, ["vxlan", "topology", "fabric_links"])

if 'fabric_links' not in check['keys_data']:
Expand All @@ -135,13 +178,27 @@ def validate_fabric_links(cls, inventory, vpc_peers_list):
fabric_links_name = f"{source_device}-{dest_device}"
fabric_links_list.append(fabric_links_name)
ipv4_config = link.get("ipv4", {})

ipv6_config = link.get("ipv6", {})
# Check IPv4 configuration
if not ipv4_config or not ipv4_config.get("subnet") or not ipv4_config.get("source_ipv4") or not ipv4_config.get("dest_ipv4"):
cls.results.append(
f"Fabric link between '{source_device}' and '{dest_device}' is missing a valid IPv4 configuration."
)

if interface_numbering_v4:
if len(ipv4_config.keys()) > 0 and (not ipv4_config.get("subnet") or not ipv4_config.get("source_ipv4") or not ipv4_config.get("dest_ipv4")):
cls.results.append(
f"Fabric link between '{source_device}' and '{dest_device}' is missing a valid IPv4 configuration."
)
if len(ipv4_config.keys()) == 0 and interface_numbering_v4.get("fabric_interface_numbering", None) == "p2p":
cls.results.append(
f"Fabric link between '{source_device}' and '{dest_device}' is missing a valid IPv4 configuration."
)
# Check IPv6 configuration
if interface_numbering_v6:
if len(ipv6_config.keys()) > 0 and (not ipv6_config.get("subnet") or not ipv6_config.get("source_ipv6") or not ipv6_config.get("dest_ipv6")):
cls.results.append(
f"Fabric link between '{source_device}' and '{dest_device}' is missing a valid IPv6 configuration."
)
if len(ipv6_config.keys()) == 0 and interface_numbering_v6["enable_ipv6_link_local_address"] is False:
cls.results.append(
f"Fabric link between '{source_device}' and '{dest_device}' is missing a valid IPv6 configuration."
)
# Check if vpc_peers is on fabric_link
vpc_peers = cls.safeget(inventory, ["vxlan", "topology", "vpc_peers"])
for peer in vpc_peers:
Expand All @@ -165,6 +222,19 @@ def check_interface_with_ipv4(cls, interfaces, loopback_name):
return True
return False

@classmethod
def check_interface_with_ipv6(cls, interfaces, loopback_name):
"""
Helper method to check if a specific loopback interface exists and has an IPv6 address.
"""
# Create a short_name to catch both values allowed in the schema and change to lower to compare them
short_name = loopback_name.replace("loopback", "lo")
for interface in interfaces:
intf = interface.get("name").lower()
if (intf == loopback_name.lower() or intf == short_name.lower()) and interface.get("ipv6_address"):
return True
return False

@classmethod
def data_model_key_check(cls, tested_object, keys):
"""
Expand Down