Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
32 changes: 32 additions & 0 deletions ansible/config_sonic_basedon_testbed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,11 @@
- name: Load variables from topology file for topo_{{ topo }}.yml
include_vars: "vars/topo_{{ topo }}.yml"

- name: set BGP confd ASN facts
set_fact:
bgp_confd_asn: "{{ hostvars[inventory_hostname]['configuration_properties']['common']['dut_confed_asn'] | default(None) }}"
bgp_confd_peers: "{{ hostvars[inventory_hostname]['configuration_properties']['common']['dut_confed_peers'] | default(None) }}"

- name: saved original minigraph file in SONiC DUT(ignore errors when file does not exist)
shell: mv /etc/sonic/minigraph.xml /etc/sonic/minigraph.xml.orig
become: true
Expand Down Expand Up @@ -611,6 +616,9 @@
topo_name: "{{ topo }}"
port_index_map: "{{ port_index_map | default({}) }}"
hwsku: "{{ hwsku }}"
num_asics: "{{ num_asics }}"
bgp_confd_asn: "{{ bgp_confd_asn }}"
bgp_confd_peers: "{{ bgp_confd_peers }}"
become: true
when: enable_macsec is not defined

Expand All @@ -631,6 +639,30 @@
topo_name: "{{ topo }}"
macsec_profile: "{{ macsec_profile }}"
num_asics: "{{ num_asics }}"
bgp_confd_asn: "{{ bgp_confd_asn }}"
bgp_confd_peers: "{{ bgp_confd_peers }}"
become: true
when: "('t2' in topo) and (enable_macsec is defined)"

- name: Copy macsec profile json to dut
copy: src=../tests/common/macsec/profile.json
dest=/tmp/profile.json
become: true
when: "('t2' in topo) and (enable_macsec is defined)"

- name: Copy golden_config_db_t2 template to DUT
copy: src=templates/golden_config_db_t2.j2
dest=/tmp/golden_config_db_t2.j2
become: true
when: "('t2' in topo) and (enable_macsec is defined)"

- name: Generate golden_config_db.json for t2
generate_golden_config_db:
topo_name: "{{ topo }}"
macsec_profile: "{{ macsec_profile }}"
num_asics: "{{ num_asics }}"
bgp_confd_asn: "{{ bgp_confd_asn }}"
bgp_confd_peers: "{{ bgp_confd_peers }}"
become: true
when: "('t2' in topo) and (enable_macsec is defined)"

Expand Down
6 changes: 6 additions & 0 deletions ansible/library/bgp_facts.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ def parse_summary(self):
def parse_neighbors(self):
regex_ipv4 = re.compile(
r'^BGP neighbor is \*?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
regex_confed_link = re.compile(r'^BGP neighbor is .*(confed-(?:internal|external) link)')
regex_ipv6 = re.compile(r'^BGP neighbor is \*?([0-9a-fA-F:]+)')
regex_remote_as = re.compile(r'.*remote AS (\d+)')
regex_local_as = re.compile(r'.*local AS (\d+)')
Expand Down Expand Up @@ -165,6 +166,11 @@ def parse_neighbors(self):
neighbor_ip = None

for line in lines:
if regex_confed_link.match(line):
neighbor['confed_peer'] = True
if neighbor['confed_peer']:
confed_peer_type = regex_confed_link.match(line).group(1)
neighbor['confed_peer_type'] = 'external' if confed_peer_type == 'confed-external link' else 'internal' # noqa: E501
if regex_ipv4.match(line):
neighbor_ip = regex_ipv4.match(line).group(1)
neighbor['ip_version'] = 4
Expand Down
106 changes: 80 additions & 26 deletions ansible/library/generate_golden_config_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,19 @@ def __init__(self):
port_index_map=dict(require=False, type='dict', default=None),
macsec_profile=dict(require=False, type='str', default=None),
num_asics=dict(require=False, type='int', default=1),
hwsku=dict(require=False, type='str', default=None)),
hwsku=dict(require=False, type='str', default=None),
bgp_confd_asn=dict(require=False, type='str', default=None),
bgp_confd_peers=dict(require=False, type='str', default=None)),
supports_check_mode=True)
self.topo_name = self.module.params['topo_name']
self.port_index_map = self.module.params['port_index_map']
self.hwsku = self.module.params['hwsku']
self.macsec_profile = self.module.params['macsec_profile']
self.num_asics = self.module.params['num_asics']
self.platform, _ = device_info.get_platform_and_hwsku()

self.bgp_confd_asn = self.module.params['bgp_confd_asn']
self.bgp_confd_peers = self.module.params['bgp_confd_peers']

def generate_mgfx_golden_config_db(self):
rc, out, err = self.module.run_command("sonic-cfggen -H -m -j /etc/sonic/init_cfg.json --print-data")
Expand Down Expand Up @@ -253,43 +259,86 @@ def generate_smartswitch_golden_config_db(self):
gold_config_db.update(smartswitch_config_obj)
return json.dumps(gold_config_db, indent=4)

def generate_ut2_golden_config_db(self):
rendered_json = {}
if self.macsec_profile:
with open(MACSEC_PROFILE_PATH) as f:
macsec_profiles = json.load(f)

profile = macsec_profiles.get(self.macsec_profile)
if profile:
profile['macsec_profile'] = self.macsec_profile

# Update the profile context with the asic count
profile['asic_cnt'] = self.num_asics

def safe_open_template(template_path):
with open(template_path) as template_file:
return Template(template_file.read())

# Render the template using the profile
rendered_json = safe_open_template(GOLDEN_CONFIG_TEMPLATE_PATH).render(profile)

if self.num_asics > 1:
if "localhost" not in rendered_json:
rendered_json["localhost"] = {}
for asic in range(0, self.num_asics):
namespace = "asic{}".format(asic)
if namespace not in rendered_json:
rendered_json[namespace] = {}
rendered_json[namespace] = {"BGP_DEVICE_GLOBAL": {"CONFED": {"asn": "65100", "peers": "65300"}}}
else:
if "BGP_DEVICE_GLOBAL" not in rendered_json:
rendered_json["BGP_DEVICE_GLOBAL"] = {}
rendered_json["BGP_DEVICE_GLOBAL"]["CONFED"] = {"asn": "65100", "peers": "65300"}
return json.dumps(rendered_json, indent=4)

def generate_t2_golden_config_db(self):
rendered_json = {}
if self.macsec_profile:
with open(MACSEC_PROFILE_PATH) as f:
macsec_profiles = json.load(f)

profile = macsec_profiles.get(self.macsec_profile)
if profile:
profile['macsec_profile'] = self.macsec_profile

# Update the profile context with the asic count
profile['asic_cnt'] = self.num_asics

def safe_open_template(template_path):
with open(template_path) as template_file:
return Template(template_file.read())

# Render the template using the profile
rendered_json = safe_open_template(GOLDEN_CONFIG_TEMPLATE_PATH).render(profile)

return rendered_json

def generate_lt2_ft2_golden_config_db(self):
"""
Generate golden_config for FT2 to enable FEC.
**Only PORT table is updated**.
Generate golden_config for FT2 to enable FEC and set BGP confed.
"""
SUPPORTED_TOPO = ["ft2-64", "lt2-p32o64", "lt2-o128"]
if self.topo_name not in SUPPORTED_TOPO:
return "{}"
SUPPORTED_PORT_SPEED = ["200000", "400000", "800000"]
ori_config = json.loads(self.get_config_from_minigraph())
port_config = ori_config.get("PORT", {})
for name, config in port_config.items():
golden_config = ori_config
golden_config["PORT"] = ori_config.get("PORT", {})
for _, config in golden_config["PORT"].items():
# Enable FEC for ports with supported speed
if config["speed"] in SUPPORTED_PORT_SPEED and "fec" not in config:
config["fec"] = "rs"

return json.dumps({"PORT": port_config}, indent=4)

def generate_t2_golden_config_db(self):
with open(MACSEC_PROFILE_PATH) as f:
macsec_profiles = json.load(f)

profile = macsec_profiles.get(self.macsec_profile)
if profile:
profile['macsec_profile'] = self.macsec_profile

# Update the profile context with the asic count
profile['asic_cnt'] = self.num_asics
# Add BGP confederation config
if self.bgp_confd_asn and self.bgp_confd_peers:
golden_config["BGP_DEVICE_GLOBAL"] = ori_config.get("BGP_DEVICE_GLOBAL", {})
# Replace space in confg_peers with ; to align with NDM
golden_config["BGP_DEVICE_GLOBAL"]["CONFED"] = \
{"asn": str(self.bgp_confd_asn), "peers": str(self.bgp_confd_peers).replace(' ', ';')}

def safe_open_template(template_path):
with open(template_path) as template_file:
return Template(template_file.read())

# Render the template using the profile
rendered_json = safe_open_template(GOLDEN_CONFIG_TEMPLATE_PATH).render(profile)

return rendered_json
return json.dumps(golden_config, indent=4)

def generate(self):
module_msg = "Success to generate golden_config_db.json"
Expand All @@ -304,7 +353,12 @@ def generate(self):
module_msg = module_msg + " for smartswitch"
elif "ft2" in self.topo_name or "lt2" in self.topo_name:
config = self.generate_lt2_ft2_golden_config_db()
elif "t2" in self.topo_name and self.macsec_profile:
elif "t2_single_node" in self.topo_name:
config = self.generate_ut2_golden_config_db()
self.module.run_command("sudo rm -f {}".format(MACSEC_PROFILE_PATH))
self.module.run_command("sudo rm -f {}".format(GOLDEN_CONFIG_TEMPLATE_PATH))
module_msg = module_msg + " for ut2 device"
elif "t2" in self.topo_name:
config = self.generate_t2_golden_config_db()
self.module.run_command("sudo rm -f {}".format(MACSEC_PROFILE_PATH))
self.module.run_command("sudo rm -f {}".format(GOLDEN_CONFIG_TEMPLATE_PATH))
Expand Down
8 changes: 6 additions & 2 deletions ansible/library/topo_facts.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,12 @@ def parse_topo_defintion(self, topo_definition, po_map, dut_num, neigh_type='VMs

# bgp
vmconfig[vm]['bgp_asn'] = topo_definition['configuration'][vm]['bgp']['asn']
dut_asn = topo_definition['configuration_properties']['common']['dut_asn']
for ipstr in topo_definition['configuration'][vm]['bgp']['peers'][dut_asn]:
peer_in_bgp_confed = topo_definition['configuration'][vm]['bgp'].get('peer_in_bgp_confed', False)
if peer_in_bgp_confed:
peer_asn = topo_definition['configuration_properties']['common']['dut_confed_asn']
else:
peer_asn = topo_definition['configuration_properties']['common']['dut_asn']
for ipstr in topo_definition['configuration'][vm]['bgp']['peers'][peer_asn]:
ip_mask = None
if '/' in ipstr:
(ipstr, ip_mask) = ipstr.split('/')
Expand Down
11 changes: 10 additions & 1 deletion ansible/module_utils/port_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,16 @@ def get_port_alias_to_name_map(hwsku, asic_name=None):
"Arista-7800R3A-36DM2-D36"]:
for i in range(1, 37):
sonic_name = "Ethernet%d" % ((i - 1) * 8)
port_alias_to_name_map["Ethernet{}/{}".format(i, 1)] = sonic_name
port_alias_to_name_map["etp{}".format(i)] = sonic_name
elif hwsku in ["Arista-7280R4-32QF-32DF-64O",
"Arista-7280R4K-32QF-32DF-64O"]:
portNum = 0
for i in range(1, 65):
port_alias_to_name_map["Ethernet{}/{}".format(i, 1)] = "Ethernet%d" % portNum
if i > 16 and i < 49:
portNum += 4
else:
portNum += 8
elif hwsku == "Arista-7800R3A-36DM2-C72" or\
hwsku == "Arista-7800R3A-36D-C72" or\
hwsku == "Arista-7800R3A-36P-C72" or\
Expand Down
9 changes: 7 additions & 2 deletions ansible/roles/eos/templates/t0-leaf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,18 @@ interface {{ bp_ifname }}
router bgp {{ host['bgp']['asn'] }}
router-id {{ host['bgp']['router-id'] if host['bgp']['router-id'] is defined else host['interfaces']['Loopback0']['ipv4'] | ansible.utils.ipaddr('address') }}
!
{% if host['bgp']['confed_asn'] is defined and host['bgp']['confed_peers'] is defined %}
bgp confederation identifier {{ host['bgp']['confed_asn'] }}
bgp confederation peers {{ host['bgp']['confed_peers'] }}
!
{% endif %}
{% for asn, remote_ips in host['bgp']['peers'].items() %}
{% for remote_ip in remote_ips %}
{% for remote_ip in remote_ips %}
neighbor {{ remote_ip }} remote-as {{ asn }}
neighbor {{ remote_ip }} description {{ asn }}
neighbor {{ remote_ip }} next-hop-self
{# set LT2/FT2 as reflector to advertise route to DUT #}
{% if props.swrole is defined and props.swrole in ("lowerspine", "fabricspine") %}
{% if props.swrole is defined and props.swrole in ("lowerspine", "fabricspine") and host['bgp']['confed_asn'] is not defined %}
neighbor {{ remote_ip }} route-reflector-client
neighbor {{ remote_ip }} additional-paths send any
{% endif %}
Expand Down
5 changes: 5 additions & 0 deletions ansible/roles/eos/templates/t1-64-lag-tor.j2
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ router bgp {{ host['bgp']['asn'] }}
graceful-restart restart-time {{ bgp_gr_timer }}
graceful-restart
!
{% if host['bgp']['confed_asn'] is defined and host['bgp']['confed_peers'] is defined %}
bgp confederation identifier {{ host['bgp']['confed_asn'] }}
bgp confederation peers {{ host['bgp']['confed_peers'] }}
!
{% endif %}
{% for asn, remote_ips in host['bgp']['peers'].items() %}
{% for remote_ip in remote_ips %}
neighbor {{ remote_ip }} remote-as {{ asn }}
Expand Down
7 changes: 6 additions & 1 deletion ansible/roles/eos/templates/t1-lag-spine.j2
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,13 @@ interface {{ bp_ifname }}
router bgp {{ host['bgp']['asn'] }}
router-id {{ host['bgp']['router-id'] if host['bgp']['router-id'] is defined else host['interfaces']['Loopback0']['ipv4'] | ansible.utils.ipaddr('address') }}
!
{% if host['bgp']['confed_asn'] is defined and host['bgp']['confed_peers'] is defined %}
bgp confederation identifier {{ host['bgp']['confed_asn'] }}
bgp confederation peers {{ host['bgp']['confed_peers'] }}
!
{% endif %}
{% for asn, remote_ips in host['bgp']['peers'].items() %}
{% for remote_ip in remote_ips %}
{% for remote_ip in remote_ips %}
neighbor {{ remote_ip }} remote-as {{ asn }}
neighbor {{ remote_ip }} description {{ asn }}
neighbor {{ remote_ip }} next-hop-self
Expand Down
6 changes: 6 additions & 0 deletions ansible/roles/eos/templates/t2-leaf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ interface {{ bp_ifname }}
router bgp {{ host['bgp']['asn'] }}
router-id {{ host['interfaces']['Loopback0']['ipv4'] | ansible.utils.ipaddr('address') }}
!
{% if host['bgp']['confed_asn'] is defined and host['bgp']['confed_peers'] is defined %}
bgp confederation identifier {{ host['bgp']['confed_asn'] }}
bgp confederation peers {{ host['bgp']['confed_peers'] }}
!
{% endif %}

{% for asn, remote_ips in host['bgp']['peers'].items() %}
{% for remote_ip in remote_ips %}
neighbor {{ remote_ip }} remote-as {{ asn }}
Expand Down
3 changes: 3 additions & 0 deletions tests/bgp/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,9 @@ def bgpmon_setup_teardown(ptfhost, duthosts, enum_rand_one_per_hwsku_frontend_ho
peer_addr = connection['neighbor_addr'].split("/")[0]
mg_facts = duthost.minigraph_facts(host=duthost.hostname)['ansible_facts']
asn = mg_facts['minigraph_bgp_asn']
confed_asn = duthost.get_bgp_confed_asn()
if confed_asn:
asn = confed_asn
# TODO: Add a common method to load BGPMON config for test_bgpmon and test_traffic_shift
logger.info("Configuring bgp monitor session on DUT")
bgpmon_args = {
Expand Down
13 changes: 11 additions & 2 deletions tests/bgp/route_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ def parse_routes_on_eos(dut_host, neigh_hosts, ip_ver, exp_community=[]):
"""
mg_facts = dut_host.minigraph_facts(
host=dut_host.hostname)['ansible_facts']
asn = mg_facts['minigraph_bgp_asn']
confed_asn = dut_host.get_bgp_confed_asn()

all_routes = {}
BGP_ENTRY_HEADING = r"BGP routing table entry for "
BGP_COMMUNITY_HEADING = r"Community: "
Expand All @@ -83,6 +84,10 @@ def parse_routes_process(node=None, results=None, my_community=exp_community):
# get hostname('ARISTA11T0') by VM name('VM0122')
hostname = host_name_map[node['host'].hostname]
host = node['host']
peer_in_bgp_confed = node['conf']['bgp'].get('peer_in_bgp_confed', False)
asn = mg_facts['minigraph_bgp_asn']
if peer_in_bgp_confed:
asn = confed_asn
peer_ips = node['conf']['bgp']['peers'][asn]
for ip in peer_ips:
if ipaddress.IPNetwork(ip).version == 4:
Expand Down Expand Up @@ -154,7 +159,7 @@ def parse_routes_process(node=None, results=None, my_community=exp_community):
def parse_routes_on_vsonic(dut_host, neigh_hosts, ip_ver):
mg_facts = dut_host.minigraph_facts(
host=dut_host.hostname)['ansible_facts']
asn = mg_facts['minigraph_bgp_asn']
confed_asn = dut_host.get_bgp_confed_asn()
all_routes = {}

host_name_map = {}
Expand All @@ -164,6 +169,10 @@ def parse_routes_on_vsonic(dut_host, neigh_hosts, ip_ver):
def parse_routes_process_vsonic(node=None, results=None):
hostname = host_name_map[node['host'].hostname]
host = node['host']
peer_in_bgp_confed = node['conf']['bgp'].get('peer_in_bgp_confed', False)
asn = mg_facts['minigraph_bgp_asn']
if peer_in_bgp_confed:
asn = confed_asn
peer_ips = node['conf']['bgp']['peers'][asn]

for ip in peer_ips:
Expand Down
11 changes: 10 additions & 1 deletion tests/common/helpers/bgp.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,20 @@ def run_bgp_facts(duthost, enum_asic_index):
namespace = duthost.get_namespace_from_asic_id(enum_asic_index)
config_facts = duthost.config_facts(host=duthost.hostname, source="running", namespace=namespace)['ansible_facts']
sonic_db_cmd = "sonic-db-cli {}".format("-n " + namespace if namespace else "")
bgp_confed_asn = config_facts.get('BGP_DEVICE_GLOBAL', {}).get('CONFED', {}).get('asn', None)
bgp_asn = int(config_facts['DEVICE_METADATA']['localhost']['bgp_asn'].encode().decode("utf-8"))
for k, v in list(bgp_facts['bgp_neighbors'].items()):
# Verify bgp sessions are established
assert v['state'] == 'established'
# Verify local ASNs in bgp sessions
assert v['local AS'] == int(config_facts['DEVICE_METADATA']['localhost']['bgp_asn'].encode().decode("utf-8"))
confed_peer = v.get('confed_peer', False)
if bgp_confed_asn:
if confed_peer:
assert v['local AS'] == int(bgp_asn)
else:
assert v['local AS'] == int(bgp_confed_asn)
else:
assert v['local AS'] == bgp_asn
# Check bgpmon functionality by validate STATE DB contains this neighbor as well
state_fact = duthost.shell('{} STATE_DB HGET "NEIGH_STATE_TABLE|{}" "state"'
.format(sonic_db_cmd, k), module_ignore_errors=False)['stdout_lines']
Expand Down