[master]staticroutebfd process implementation (#13789)
* [BFD] staticroutebfd implementation * To enable the BFD for static route HLD: sonic-net/SONiC#1216
This commit is contained in:
parent
a09048acd5
commit
359b80e012
@ -9,4 +9,5 @@ program:pimd
|
||||
program:frrcfgd
|
||||
{%- else %}
|
||||
program:bgpcfgd
|
||||
program:staticroutebfd
|
||||
{%- endif %}
|
||||
|
@ -141,6 +141,20 @@ stderr_logfile=syslog
|
||||
dependent_startup=true
|
||||
dependent_startup_wait_for=bgpd:running
|
||||
|
||||
{% if DEVICE_METADATA.localhost.frr_mgmt_framework_config is defined and DEVICE_METADATA.localhost.frr_mgmt_framework_config == "true" %}
|
||||
{% else %}
|
||||
[program:staticroutebfd]
|
||||
command=/usr/local/bin/staticroutebfd
|
||||
priority=6
|
||||
autostart=false
|
||||
autorestart=false
|
||||
startsecs=0
|
||||
stdout_logfile=syslog
|
||||
stderr_logfile=syslog
|
||||
dependent_startup=true
|
||||
dependent_startup_wait_for=bgpd:running
|
||||
{% endif %}
|
||||
|
||||
[program:bgpmon]
|
||||
command=/usr/local/bin/bgpmon
|
||||
priority=6
|
||||
|
@ -25,6 +25,7 @@ class StaticRouteMgr(Manager):
|
||||
self.directory.subscribe([("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost/bgp_asn"),], self.on_bgp_asn_change)
|
||||
self.static_routes = {}
|
||||
self.vrf_pending_redistribution = set()
|
||||
self.config_db = None
|
||||
|
||||
OP_DELETE = 'DELETE'
|
||||
OP_ADD = 'ADD'
|
||||
@ -41,7 +42,17 @@ class StaticRouteMgr(Manager):
|
||||
intf_list = arg_list(data['ifname']) if 'ifname' in data else None
|
||||
dist_list = arg_list(data['distance']) if 'distance' in data else None
|
||||
nh_vrf_list = arg_list(data['nexthop-vrf']) if 'nexthop-vrf' in data else None
|
||||
route_tag = self.ROUTE_ADVERTISE_DISABLE_TAG if 'advertise' in data and data['advertise'] == "false" else self.ROUTE_ADVERTISE_ENABLE_TAG
|
||||
bfd_enable = arg_list(data['bfd']) if 'bfd' in data else None
|
||||
route_tag = self.ROUTE_ADVERTISE_DISABLE_TAG if 'advertise' in data and data['advertise'] == "false" else self.ROUTE_ADVERTISE_ENABLE_TAG
|
||||
|
||||
# bfd enabled route would be handled in staticroutebfd, skip here
|
||||
if bfd_enable and bfd_enable[0].lower() == "true":
|
||||
log_debug("{} static route {} bfd flag is true".format(self.db_name, key))
|
||||
tmp_nh_set, tmp_route_tag = self.static_routes.get(vrf, {}).get(ip_prefix, (IpNextHopSet(is_ipv6), route_tag))
|
||||
if tmp_nh_set: #clear nexthop set if it is not empty
|
||||
log_debug("{} static route {} bfd flag is true, cur_nh is not empty, clear it".format(self.db_name, key))
|
||||
self.static_routes.setdefault(vrf, {}).pop(ip_prefix, None)
|
||||
return True
|
||||
|
||||
try:
|
||||
ip_nh_set = IpNextHopSet(is_ipv6, bkh_list, nh_list, intf_list, dist_list, nh_vrf_list)
|
||||
@ -60,19 +71,59 @@ class StaticRouteMgr(Manager):
|
||||
|
||||
if cmd_list:
|
||||
self.cfg_mgr.push_list(cmd_list)
|
||||
log_debug("Static route {} is scheduled for updates".format(key))
|
||||
log_debug("{} Static route {} is scheduled for updates. {}".format(self.db_name, key, str(cmd_list)))
|
||||
else:
|
||||
log_debug("Nothing to update for static route {}".format(key))
|
||||
log_debug("{} Nothing to update for static route {}".format(self.db_name, key))
|
||||
|
||||
self.static_routes.setdefault(vrf, {})[ip_prefix] = (ip_nh_set, route_tag)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def skip_appl_del(self, vrf, ip_prefix):
|
||||
"""
|
||||
If a static route is bfd enabled, the processed static route is written into application DB by staticroutebfd.
|
||||
When we disable bfd for that route at runtime, that static route entry will be removed from APPL_DB STATIC_ROUTE_TABLE.
|
||||
In the case, the StaticRouteMgr(appl_db) cannot uninstall the static route from FRR if the static route is still in CONFIG_DB,
|
||||
so need this checking (skip appl_db deletion) to avoid race condition between StaticRouteMgr(appl_db) and StaticRouteMgr(config_db)
|
||||
For more detailed information:
|
||||
https://github.com/sonic-net/SONiC/blob/master/doc/static-route/SONiC_static_route_bfd_hld.md#bfd-field-changes-from-true-to-false
|
||||
|
||||
:param vrf: vrf from the split_key(key) return
|
||||
:param ip_prefix: ip_prefix from the split_key(key) return
|
||||
:return: True if the deletion comes from APPL_DB and the vrf|ip_prefix exists in CONFIG_DB, otherwise return False
|
||||
"""
|
||||
if self.db_name == "CONFIG_DB":
|
||||
return False
|
||||
|
||||
if self.config_db is None:
|
||||
self.config_db = swsscommon.SonicV2Connector()
|
||||
self.config_db.connect(self.config_db.CONFIG_DB)
|
||||
|
||||
#just pop local cache if the route exist in config_db
|
||||
cfg_key = "STATIC_ROUTE|" + vrf + "|" + ip_prefix
|
||||
nexthop = self.config_db.get(self.config_db.CONFIG_DB, cfg_key, "nexthop")
|
||||
if nexthop and len(nexthop)>0:
|
||||
self.static_routes.setdefault(vrf, {}).pop(ip_prefix, None)
|
||||
return True
|
||||
|
||||
if vrf == "default":
|
||||
cfg_key = "STATIC_ROUTE|" + ip_prefix
|
||||
nexthop = self.config_db.get(self.config_db.CONFIG_DB, cfg_key, "nexthop")
|
||||
if nexthop and len(nexthop)>0:
|
||||
self.static_routes.setdefault(vrf, {}).pop(ip_prefix, None)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def del_handler(self, key):
|
||||
vrf, ip_prefix = self.split_key(key)
|
||||
is_ipv6 = TemplateFabric.is_ipv6(ip_prefix)
|
||||
|
||||
if self.skip_appl_del(vrf, ip_prefix):
|
||||
log_debug("{} ignore appl_db static route deletion because of key {} exist in config_db".format(self.db_name, key))
|
||||
return
|
||||
|
||||
ip_nh_set = IpNextHopSet(is_ipv6)
|
||||
cur_nh_set, route_tag = self.static_routes.get(vrf, {}).get(ip_prefix, (IpNextHopSet(is_ipv6), self.ROUTE_ADVERTISE_DISABLE_TAG))
|
||||
cmd_list = self.static_route_commands(ip_nh_set, cur_nh_set, ip_prefix, vrf, route_tag, route_tag)
|
||||
@ -85,9 +136,9 @@ class StaticRouteMgr(Manager):
|
||||
|
||||
if cmd_list:
|
||||
self.cfg_mgr.push_list(cmd_list)
|
||||
log_debug("Static route {} is scheduled for updates".format(key))
|
||||
log_debug("{} Static route {} is scheduled for updates. {}".format(self.db_name, key, str(cmd_list)))
|
||||
else:
|
||||
log_debug("Nothing to update for static route {}".format(key))
|
||||
log_debug("{} Nothing to update for static route {}".format(self.db_name, key))
|
||||
|
||||
self.static_routes.setdefault(vrf, {}).pop(ip_prefix, None)
|
||||
|
||||
|
@ -11,6 +11,7 @@ setuptools.setup(
|
||||
entry_points = {
|
||||
'console_scripts': [
|
||||
'bgpcfgd = bgpcfgd.main:main',
|
||||
'staticroutebfd = staticroutebfd.main:main',
|
||||
'bgpmon = bgpmon.bgpmon:main',
|
||||
]
|
||||
},
|
||||
|
0
src/sonic-bgpcfgd/staticroutebfd/__init__.py
Normal file
0
src/sonic-bgpcfgd/staticroutebfd/__init__.py
Normal file
764
src/sonic-bgpcfgd/staticroutebfd/main.py
Normal file
764
src/sonic-bgpcfgd/staticroutebfd/main.py
Normal file
@ -0,0 +1,764 @@
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import syslog
|
||||
import threading
|
||||
import traceback
|
||||
from collections import defaultdict
|
||||
from ipaddress import IPv4Address, IPv6Address
|
||||
from copy import deepcopy
|
||||
|
||||
from swsscommon import swsscommon
|
||||
|
||||
from .vars import g_debug, bfd_multihop, bfd_rx_interval, bfd_tx_interval, bfd_multiplier
|
||||
|
||||
g_run = True
|
||||
|
||||
CONFIG_DB_NAME = "CONFIG_DB"
|
||||
APPL_DB_NAME = "APPL_DB"
|
||||
STATE_DB_NAME = "STATE_DB"
|
||||
|
||||
INTERFACE_TABLE_NAME = "INTERFACE"
|
||||
PORTCHANNEL_INTERFACE_TABLE_NAME = "PORTCHANNEL_INTERFACE"
|
||||
STATIC_ROUTE_TABLE_NAME = "STATIC_ROUTE"
|
||||
BFD_SESSION_TABLE_NAME = "BFD_SESSION_TABLE"
|
||||
|
||||
LOCAL_CONFIG_TABLE = "config"
|
||||
LOCAL_NEXTHOP_TABLE = "nexthop"
|
||||
LOCAL_SRT_TABLE = "srt"
|
||||
LOCAL_BFD_TABLE = "bfd"
|
||||
LOCAL_BFD_PENDING_TABLE = "bfd_pending"
|
||||
LOCAL_INTERFACE_TABLE = "interface"
|
||||
|
||||
def log_debug(msg):
|
||||
""" Send a message msg to the syslog as DEBUG """
|
||||
if g_debug:
|
||||
syslog.syslog(syslog.LOG_DEBUG, msg)
|
||||
|
||||
def log_notice(msg):
|
||||
""" Send a message msg to the syslog as NOTICE """
|
||||
syslog.syslog(syslog.LOG_NOTICE, msg)
|
||||
|
||||
def log_info(msg):
|
||||
""" Send a message msg to the syslog as INFO """
|
||||
syslog.syslog(syslog.LOG_INFO, msg)
|
||||
|
||||
def log_warn(msg):
|
||||
""" Send a message msg to the syslog as WARNING """
|
||||
syslog.syslog(syslog.LOG_WARNING, msg)
|
||||
|
||||
def log_err(msg):
|
||||
""" Send a message msg to the syslog as ERR """
|
||||
syslog.syslog(syslog.LOG_ERR, msg)
|
||||
|
||||
def log_crit(msg):
|
||||
""" Send a message msg to the syslog as CRIT """
|
||||
syslog.syslog(syslog.LOG_CRIT, msg)
|
||||
|
||||
def signal_handler(_, __): # signal_handler(signum, frame)
|
||||
""" signal handler """
|
||||
global g_run
|
||||
g_run = False
|
||||
|
||||
def static_route_split_key(key):
|
||||
"""
|
||||
Split key into vrf name and prefix.
|
||||
:param key: key to split
|
||||
:return: valid, vrf name extracted from the key, ip prefix extracted from the key
|
||||
"""
|
||||
l = tuple(key.split('|'))
|
||||
|
||||
if len(l) == 1:
|
||||
return True, 'default', l[0]
|
||||
|
||||
return True, l[0], l[1]
|
||||
|
||||
def check_ip(ip):
|
||||
if len(ip) == 0:
|
||||
return False, False, ""
|
||||
|
||||
value = ip.split('/',1)
|
||||
v = value[0]
|
||||
try:
|
||||
IPv4Address(v)
|
||||
valid = True
|
||||
is_ipv4 = True
|
||||
except:
|
||||
is_ipv4 = False
|
||||
try:
|
||||
IPv6Address(v)
|
||||
valid = True
|
||||
except:
|
||||
valid = False
|
||||
return valid, is_ipv4, v
|
||||
|
||||
class StaticRouteBfd(object):
|
||||
|
||||
SELECT_TIMEOUT = 1000
|
||||
BFD_DEFAULT_CFG = {"multihop": "false", "rx_interval": "50", "tx_interval": "50"}
|
||||
|
||||
def __init__(self):
|
||||
self.local_db = defaultdict(dict)
|
||||
self.local_db[LOCAL_CONFIG_TABLE] = defaultdict(dict)
|
||||
self.local_db[LOCAL_NEXTHOP_TABLE] = defaultdict(set)
|
||||
self.local_db[LOCAL_SRT_TABLE] = defaultdict(set)
|
||||
self.local_db[LOCAL_BFD_TABLE] = defaultdict(dict)
|
||||
self.local_db[LOCAL_BFD_PENDING_TABLE] = defaultdict(dict)
|
||||
#interface, portchannel_interface and loopback_interface share same table, assume name is unique
|
||||
#assume only one ipv4 and/or one ipv6 for each interface
|
||||
self.local_db[LOCAL_INTERFACE_TABLE] = defaultdict(dict)
|
||||
|
||||
self.config_db = swsscommon.DBConnector(CONFIG_DB_NAME, 0, True)
|
||||
self.appl_db = swsscommon.DBConnector(APPL_DB_NAME, 0, True)
|
||||
self.state_db = swsscommon.DBConnector(STATE_DB_NAME, 0, True)
|
||||
|
||||
self.bfd_appl_tbl = swsscommon.ProducerStateTable(self.appl_db, BFD_SESSION_TABLE_NAME)
|
||||
|
||||
self.static_route_appl_tbl = swsscommon.Table(self.appl_db, STATIC_ROUTE_TABLE_NAME)
|
||||
|
||||
self.selector = swsscommon.Select()
|
||||
self.callbacks = defaultdict(lambda: defaultdict(list)) # db -> table -> handlers[]
|
||||
self.subscribers = set()
|
||||
self.first_time = True
|
||||
|
||||
def get_ip_from_key(self, key):
|
||||
"""
|
||||
Get ip address from key for LOOPBACK/INTERFACE/PORTCHANNEL_INTERFACE table
|
||||
:param key: key in the tables
|
||||
:return: valid, is_ipv4, ip address
|
||||
"""
|
||||
if '|' not in key:
|
||||
return False, False, "", ""
|
||||
else:
|
||||
if_ip = key.split('|')
|
||||
if len(if_ip) < 2:
|
||||
return False, False, "", ""
|
||||
if_name = if_ip[0]
|
||||
value = if_ip[1]
|
||||
valid, is_ipv4, ip = check_ip(value)
|
||||
log_debug("get ip from intf key: valid %s is_ipv4 %s, if_name %s ip %s"%(str(valid), str(is_ipv4), if_name, ip))
|
||||
return valid, is_ipv4, if_name, ip
|
||||
|
||||
def set_local_db(self, table, key, data):
|
||||
try:
|
||||
self.local_db[table][key] = data
|
||||
return
|
||||
except:
|
||||
log_err("set_local_db error, table %s key %s"%(table, key))
|
||||
pass
|
||||
|
||||
def get_local_db(self, table, key):
|
||||
try:
|
||||
v = self.local_db[table][key]
|
||||
return v
|
||||
except:
|
||||
return {}
|
||||
|
||||
def remove_from_local_db(self, table, key):
|
||||
if table in self.local_db:
|
||||
if key in self.local_db[table]:
|
||||
del self.local_db[table][key]
|
||||
|
||||
def append_to_nh_table_entry(self, nh_key, ip_prefix):
|
||||
entry = self.get_local_db(LOCAL_NEXTHOP_TABLE, nh_key)
|
||||
entry.add(ip_prefix)
|
||||
|
||||
def remove_from_nh_table_entry(self, nh_key, ip_prefix):
|
||||
entry = self.get_local_db(LOCAL_NEXTHOP_TABLE, nh_key)
|
||||
if ip_prefix in entry:
|
||||
entry.remove(ip_prefix)
|
||||
if len(entry) == 0:
|
||||
self.remove_from_local_db(LOCAL_NEXTHOP_TABLE, nh_key)
|
||||
|
||||
def set_bfd_session_into_appl_db(self, key, data):
|
||||
fvs = swsscommon.FieldValuePairs(list(data.items()))
|
||||
self.bfd_appl_tbl.set(key, fvs)
|
||||
log_debug("set bfd session to appl_db, key %s, data %s"%(key, str(data)))
|
||||
|
||||
def del_bfd_session_from_appl_db(self, key):
|
||||
self.bfd_appl_tbl.delete(key)
|
||||
|
||||
def interface_set_handler(self, key, data):
|
||||
valid, is_ipv4, if_name, ip = self.get_ip_from_key(key)
|
||||
if not valid:
|
||||
return True
|
||||
value = self.get_local_db(LOCAL_INTERFACE_TABLE, if_name)
|
||||
if len(value) == 0:
|
||||
value = {is_ipv4: ip}
|
||||
else:
|
||||
value[is_ipv4] = ip
|
||||
self.set_local_db(LOCAL_INTERFACE_TABLE, if_name, value)
|
||||
self.update_bfd_pending(if_name)
|
||||
return True
|
||||
|
||||
def interface_del_handler(self, key):
|
||||
valid, is_ipv4, if_name, ip = self.get_ip_from_key(key)
|
||||
if not valid:
|
||||
return True
|
||||
value = self.get_local_db(LOCAL_INTERFACE_TABLE, if_name)
|
||||
if len(value) == 0:
|
||||
return
|
||||
else:
|
||||
value[is_ipv4] = "" #remove the IP address for the interface
|
||||
self.set_local_db(LOCAL_INTERFACE_TABLE, if_name, value)
|
||||
|
||||
def find_interface_ip(self, if_name, ip_example):
|
||||
valid, is_ipv4, nh = check_ip(ip_example)
|
||||
if not valid:
|
||||
return False, ""
|
||||
|
||||
value = self.get_local_db(LOCAL_INTERFACE_TABLE, if_name)
|
||||
ip = value.get(is_ipv4, "")
|
||||
if len(ip)>0: #ip should be verified when add to local_db
|
||||
return True, ip
|
||||
|
||||
return False, ""
|
||||
|
||||
def update_bfd_pending(self, if_name):
|
||||
del_list=[]
|
||||
for k, v in self.local_db[LOCAL_BFD_PENDING_TABLE].items():
|
||||
if len(v) == 3 and v[0] == if_name:
|
||||
intf, nh_ip, bfd_key = v[0], v[1], v[2]
|
||||
valid, local_addr = self.find_interface_ip(intf, nh_ip)
|
||||
if not valid: #IP address might not be available for this type of nh_ip (IPv4 or IPv6) yet
|
||||
continue
|
||||
log_notice("bfd_pending: get ip for interface: %s, create bfd session for %s"%(intf, bfd_key))
|
||||
bfd_entry_cfg = self.BFD_DEFAULT_CFG.copy()
|
||||
if all([bfd_rx_interval, bfd_tx_interval, bfd_multiplier, bfd_multihop]):
|
||||
bfd_entry_cfg["multihop"] = bfd_multihop
|
||||
bfd_entry_cfg["rx_interval"] = bfd_rx_interval
|
||||
bfd_entry_cfg["tx_interval"] = bfd_tx_interval
|
||||
bfd_entry_cfg["multiplier"] = bfd_multiplier
|
||||
|
||||
bfd_entry_cfg["local_addr"] = local_addr
|
||||
self.set_bfd_session_into_appl_db(bfd_key, bfd_entry_cfg)
|
||||
bfd_entry_cfg["static_route"] = "true"
|
||||
self.set_local_db(LOCAL_BFD_TABLE, bfd_key, bfd_entry_cfg)
|
||||
del_list.append(k)
|
||||
|
||||
for k in del_list:
|
||||
self.local_db[LOCAL_BFD_PENDING_TABLE].pop(k)
|
||||
|
||||
def strip_table_name(self, key, splitter):
|
||||
return key.split(splitter, 1)[1]
|
||||
|
||||
def reconciliation(self):
|
||||
#to use SonicV2Connector get_all method, DBConnector doesn't have get_all
|
||||
db = swsscommon.SonicV2Connector()
|
||||
db.connect(db.CONFIG_DB)
|
||||
db.connect(db.APPL_DB)
|
||||
db.connect(db.STATE_DB)
|
||||
|
||||
#MUST keep the restore sequene
|
||||
#restore interface(loopback/interface/portchannel_interface) tables
|
||||
|
||||
#restore interface tables
|
||||
log_info("restore interface table -->")
|
||||
keys = db.keys(db.CONFIG_DB, "LOOPBACK_INTERFACE|*")
|
||||
for key in keys:
|
||||
key_new = self.strip_table_name(key, "|")
|
||||
self.interface_set_handler(key_new, "")
|
||||
keys = db.keys(db.CONFIG_DB, "INTERFACE|*")
|
||||
for key in keys:
|
||||
key_new = self.strip_table_name(key, "|")
|
||||
self.interface_set_handler(key_new, "")
|
||||
keys = db.keys(db.CONFIG_DB, "PORTCHANNEL_INTERFACE|*")
|
||||
for key in keys:
|
||||
key_new = self.strip_table_name(key, "|")
|
||||
self.interface_set_handler(key_new, "")
|
||||
|
||||
#restore bfd session table, static route won't create bfd session if it is already in appl_db
|
||||
log_info("restore bfd session table -->")
|
||||
keys = db.keys(db.APPL_DB, "BFD_SESSION_TABLE:*")
|
||||
for key in keys:
|
||||
data = db.get_all(db.APPL_DB, key)
|
||||
key_new = self.strip_table_name(key, ":")
|
||||
self.set_local_db(LOCAL_BFD_TABLE, key_new, data)
|
||||
|
||||
#restore static route table
|
||||
log_info("restore static route table -->")
|
||||
keys = db.keys(db.CONFIG_DB, "STATIC_ROUTE|*")
|
||||
for key in keys:
|
||||
data = db.get_all(db.CONFIG_DB, key)
|
||||
key_new = self.strip_table_name(key, "|")
|
||||
log_debug("SRT_BFD: restore static route from config_db, key %s, data %s"%(key, str(data)))
|
||||
self.static_route_set_handler(key_new, data)
|
||||
|
||||
#clean up local bfd table, remove non static route bfd session
|
||||
log_info("cleanup bfd session table -->")
|
||||
self.cleanup_local_bfd_table()
|
||||
|
||||
#restore bfd state table
|
||||
log_info("restore bfd state table -->")
|
||||
keys = db.keys(db.STATE_DB, "BFD_SESSION_TABLE|*")
|
||||
for key in keys:
|
||||
data = db.get_all(db.STATE_DB, key)
|
||||
key_new = self.strip_table_name(key, "|")
|
||||
self.bfd_state_set_handler(key_new, data)
|
||||
|
||||
def cleanup_local_bfd_table(self):
|
||||
kl=[]
|
||||
for key in self.local_db[LOCAL_BFD_TABLE]:
|
||||
kl.append(key)
|
||||
for key in kl:
|
||||
bfd_session = self.local_db[LOCAL_BFD_TABLE][key]
|
||||
if "static_route" not in bfd_session or bfd_session["static_route"] != "true":
|
||||
self.local_db[LOCAL_BFD_TABLE].pop(key)
|
||||
|
||||
def isFieldTrue(self, bfd_field):
|
||||
if isinstance(bfd_field, list):
|
||||
if len(bfd_field) == 1:
|
||||
if isinstance(bfd_field[0], str):
|
||||
if bfd_field[0].lower() == "true":
|
||||
return True
|
||||
return False
|
||||
|
||||
def refresh_active_nh(self, route_cfg_key):
|
||||
data = self.get_local_db(LOCAL_CONFIG_TABLE, route_cfg_key)
|
||||
|
||||
arg_list = lambda v: [x.strip() for x in v.split(',')] if len(v.strip()) != 0 else None
|
||||
nh_list = arg_list(data['nexthop']) if 'nexthop' in data else None
|
||||
nh_vrf_list = arg_list(data['nexthop-vrf']) if 'nexthop-vrf' in data else None
|
||||
nh_cnt = 0
|
||||
|
||||
for index in range(len(nh_list)):
|
||||
nh_ip = nh_list[index]
|
||||
nh_vrf = nh_vrf_list[index]
|
||||
nh_key = nh_vrf + "|" + nh_ip
|
||||
bfd_key = nh_vrf + ":default:" + nh_ip
|
||||
|
||||
bfd_session = self.get_local_db(LOCAL_BFD_TABLE, bfd_key)
|
||||
if len(bfd_session) == 0:
|
||||
continue
|
||||
if "state" in bfd_session and bfd_session["state"].upper() == "UP":
|
||||
self.append_to_srt_table_entry(route_cfg_key, (nh_vrf, nh_ip))
|
||||
nh_cnt += 1
|
||||
|
||||
#do not write to appl_db is no nexthop reachable
|
||||
if nh_cnt == 0:
|
||||
return
|
||||
|
||||
#if there is any bfd session state UP, we don't need to hold the static route update.
|
||||
data['bfd_nh_hold'] = "false"
|
||||
new_config = self.reconstruct_static_route_config(data, self.get_local_db(LOCAL_SRT_TABLE, route_cfg_key))
|
||||
self.set_static_route_into_appl_db(route_cfg_key.replace("|", ":"), new_config)
|
||||
|
||||
def handle_bfd_change(self, cfg_key, data, to_bfd_enable):
|
||||
valid, vrf, ip_prefix = static_route_split_key(cfg_key)
|
||||
key = vrf + ":" + ip_prefix
|
||||
log_debug("SRT_BFD: handle_bfd_change. key %s, data %s, to_bfd_enable %s"%(key, str(data), str(to_bfd_enable)))
|
||||
if to_bfd_enable:
|
||||
#write route with full_nh_list to appl_db, let StaticRouteMgr(appl_db) install this route to update its cache
|
||||
data['bfd'] = "false"
|
||||
data['expiry'] = "false"
|
||||
self.set_static_route_into_appl_db(key, data)
|
||||
log_debug("SRT_BFD: bfd toggle to true. write the route to appl_db, update StaticRouteMgr(appl_db), key %s"%(key))
|
||||
else:
|
||||
self.del_static_route_from_appl_db(key)
|
||||
log_debug("SRT_BFD: bfd toggle to false. delete static route from appl_db, key %s"%(key))
|
||||
|
||||
#treat it as static route deletion, but do not delete it from LOCAL_CONFIG_TABLE
|
||||
self.static_route_del_handler(cfg_key, False)
|
||||
|
||||
def static_route_set_handler(self, key, data):
|
||||
|
||||
#sanity checking
|
||||
if len(data) == 0:
|
||||
return True
|
||||
|
||||
valid, vrf, ip_prefix = static_route_split_key(key)
|
||||
route_cfg_key = vrf + "|" + ip_prefix
|
||||
if not valid:
|
||||
return True
|
||||
|
||||
valid, is_ipv4, ip = check_ip(ip_prefix)
|
||||
if not valid:
|
||||
log_err("invalid ip prefix for static route: ", key)
|
||||
return True
|
||||
|
||||
arg_list = lambda v: [x.strip() for x in v.split(',')] if len(v.strip()) != 0 else None
|
||||
bfd_field = arg_list(data['bfd']) if 'bfd' in data else ["false"]
|
||||
|
||||
cur_data = self.get_local_db(LOCAL_CONFIG_TABLE, route_cfg_key)
|
||||
cur_bfd_enabled = False
|
||||
if cur_data:
|
||||
cur_bfd_field = arg_list(cur_data['bfd']) if 'bfd' in cur_data else ["false"]
|
||||
cur_bfd_enabled = self.isFieldTrue(cur_bfd_field)
|
||||
|
||||
# this process, staticroutebfd, only handle the bfd enabled case, other cases would be handled in bgpcfgd/StaticRouteMgr
|
||||
bfd_enabled = self.isFieldTrue(bfd_field)
|
||||
|
||||
#when bfd changed from "false" to "true", before bfd session created and state becomes up,
|
||||
#the installed static route need to be kept in the system system, so put this route in "hold" state until at least one
|
||||
#bfd session becomes UP.
|
||||
data_copy = data.copy()
|
||||
data['bfd_nh_hold'] = "false"
|
||||
if cur_data:
|
||||
if cur_bfd_enabled and not bfd_enabled: # dynamic bfd flag change from TRUE to FALSE
|
||||
self.handle_bfd_change(key, data_copy, False)
|
||||
if not cur_bfd_enabled and bfd_enabled: # dynamic bfd flag change from FALSE to TRUE
|
||||
self.handle_bfd_change(key, data_copy, True)
|
||||
data['bfd_nh_hold'] = "true"
|
||||
|
||||
if not bfd_enabled:
|
||||
#skip if bfd is not enabled, but store it to local_db to detect bfd field dynamic change
|
||||
data['bfd'] = "false"
|
||||
self.set_local_db(LOCAL_CONFIG_TABLE, route_cfg_key, data)
|
||||
return True
|
||||
|
||||
bkh_list = arg_list(data['blackhole']) if 'blackhole' in data else None
|
||||
nh_list = arg_list(data['nexthop']) if 'nexthop' in data else None
|
||||
intf_list = arg_list(data['ifname']) if 'ifname' in data else None
|
||||
dist_list = arg_list(data['distance']) if 'distance' in data else None
|
||||
nh_vrf_list = arg_list(data['nexthop-vrf']) if 'nexthop-vrf' in data else None
|
||||
if nh_vrf_list is None:
|
||||
nh_vrf_list = [vrf] * len(nh_list) if len(nh_list) > 0 else None
|
||||
data['nexthop-vrf'] = ','.join(nh_vrf_list) if nh_vrf_list else ''
|
||||
if intf_list is None or nh_list is None or nh_vrf_list is None or \
|
||||
len(intf_list) != len(nh_list) or len(intf_list) != len(nh_vrf_list):
|
||||
log_err("Static route bfd set Failed, nexthop, interface and vrf lists do not match.")
|
||||
return True
|
||||
|
||||
|
||||
if cur_data and cur_bfd_enabled:
|
||||
# route with the prefix already exist, remove the deleted nexthops
|
||||
nh_list_exist = arg_list(cur_data['nexthop']) if 'nexthop' in cur_data else None
|
||||
nh_vrf_list_exist = arg_list(cur_data['nexthop-vrf']) if 'nexthop-vrf' in cur_data else None
|
||||
if nh_vrf_list_exist is None:
|
||||
nh_vrf_list_exist = []
|
||||
for nh in nh_list:
|
||||
nh_vrf_list_exist.append(vrf)
|
||||
|
||||
intf_list_exist = arg_list(cur_data['ifname']) if 'ifname' in cur_data else None
|
||||
nh_key_list_exist = list(zip(nh_vrf_list_exist, intf_list_exist, nh_list_exist))
|
||||
nh_key_list_new = list(zip(nh_vrf_list, intf_list, nh_list))
|
||||
for nh in nh_key_list_exist:
|
||||
if nh not in nh_key_list_new:
|
||||
nh_vrf = nh[0]
|
||||
nh_ip = nh[2]
|
||||
nh_key = nh_vrf + "|" + nh_ip
|
||||
self.remove_from_srt_table_entry(route_cfg_key, (nh_vrf, nh_ip))
|
||||
self.remove_from_nh_table_entry(nh_key, route_cfg_key)
|
||||
if len(self.get_local_db(LOCAL_NEXTHOP_TABLE, nh_key)) == 0:
|
||||
bfd_key = nh_vrf + ":default:" + nh_ip
|
||||
self.remove_from_local_db(LOCAL_BFD_TABLE, bfd_key)
|
||||
self.del_bfd_session_from_appl_db(bfd_key)
|
||||
|
||||
self.set_local_db(LOCAL_CONFIG_TABLE, route_cfg_key, data)
|
||||
for index in range(len(nh_list)):
|
||||
nh_ip = nh_list[index]
|
||||
intf = intf_list[index]
|
||||
nh_vrf = nh_vrf_list[index]
|
||||
nh_key = nh_vrf + "|" + nh_ip
|
||||
|
||||
#check if the bfd session is already created
|
||||
bfd_key = nh_vrf + ":default:" + nh_ip
|
||||
bfd_session = self.get_local_db(LOCAL_BFD_TABLE, bfd_key)
|
||||
if len(bfd_session)>0:
|
||||
self.local_db[LOCAL_BFD_TABLE][bfd_key]["static_route"] = "true"
|
||||
|
||||
if len(self.get_local_db(LOCAL_NEXTHOP_TABLE, nh_key)) == 0 and len(bfd_session) == 0:
|
||||
valid, local_addr = self.find_interface_ip(intf, nh_ip)
|
||||
if not valid:
|
||||
#interface IP is not available yet, put this request to cache
|
||||
self.set_local_db(LOCAL_BFD_PENDING_TABLE, intf+"_"+bfd_key, [intf, nh_ip, bfd_key])
|
||||
self.append_to_nh_table_entry(nh_key, vrf + "|" + ip_prefix)
|
||||
log_warn("bfd_pending: cannot find ip for interface: %s, postpone bfd session creation" %intf)
|
||||
continue
|
||||
|
||||
bfd_entry_cfg = self.BFD_DEFAULT_CFG.copy()
|
||||
if all([bfd_rx_interval, bfd_tx_interval, bfd_multiplier, bfd_multihop]):
|
||||
bfd_entry_cfg["multihop"] = bfd_multihop
|
||||
bfd_entry_cfg["rx_interval"] = bfd_rx_interval
|
||||
bfd_entry_cfg["tx_interval"] = bfd_tx_interval
|
||||
bfd_entry_cfg["multiplier"] = bfd_multiplier
|
||||
|
||||
bfd_entry_cfg["local_addr"] = local_addr
|
||||
self.set_bfd_session_into_appl_db(bfd_key, bfd_entry_cfg)
|
||||
bfd_entry_cfg["static_route"] = "true"
|
||||
self.set_local_db(LOCAL_BFD_TABLE, bfd_key, bfd_entry_cfg)
|
||||
|
||||
self.append_to_nh_table_entry(nh_key, vrf + "|" + ip_prefix)
|
||||
|
||||
self.refresh_active_nh(route_cfg_key)
|
||||
|
||||
return True
|
||||
|
||||
def static_route_del_handler(self, key, redis_del):
|
||||
valid, vrf, ip_prefix = static_route_split_key(key)
|
||||
if not valid:
|
||||
return True
|
||||
route_cfg_key = vrf + "|" + ip_prefix
|
||||
|
||||
valid, is_ipv4, ip = check_ip(ip_prefix)
|
||||
if not valid:
|
||||
return True
|
||||
|
||||
data = self.get_local_db(LOCAL_CONFIG_TABLE, route_cfg_key)
|
||||
if len(data) == 0:
|
||||
# this route is not handled by StaticRouteBfd, skip
|
||||
return True
|
||||
|
||||
arg_list = lambda v: [x.strip() for x in v.split(',')] if len(v.strip()) != 0 else None
|
||||
nh_list = arg_list(data['nexthop']) if 'nexthop' in data else None
|
||||
nh_vrf_list = arg_list(data['nexthop-vrf']) if 'nexthop-vrf' in data else None
|
||||
for index in range(len(nh_list)):
|
||||
nh_ip = nh_list[index]
|
||||
nh_vrf = nh_vrf_list[index]
|
||||
nh_key = nh_vrf + "|" + nh_ip
|
||||
self.remove_from_nh_table_entry(nh_key, route_cfg_key)
|
||||
|
||||
if len(self.get_local_db(LOCAL_NEXTHOP_TABLE, nh_key)) == 0:
|
||||
bfd_key = nh_vrf + ":default:" + nh_ip
|
||||
self.remove_from_local_db(LOCAL_BFD_TABLE, bfd_key)
|
||||
self.del_bfd_session_from_appl_db(bfd_key)
|
||||
|
||||
self.del_static_route_from_appl_db(route_cfg_key.replace("|", ":"))
|
||||
self.remove_from_local_db(LOCAL_SRT_TABLE, route_cfg_key)
|
||||
|
||||
if redis_del:
|
||||
self.remove_from_local_db(LOCAL_CONFIG_TABLE, route_cfg_key)
|
||||
|
||||
return True
|
||||
|
||||
def interface_callback(self, key, op, data):
|
||||
if op == swsscommon.SET_COMMAND:
|
||||
self.interface_set_handler(key, data)
|
||||
elif op == swsscommon.DEL_COMMAND:
|
||||
self.interface_del_handler(key)
|
||||
else:
|
||||
log_err("Invalid operation '%s' for key '%s'" % (op, key))
|
||||
|
||||
def static_route_callback(self, key, op, data):
|
||||
if op == swsscommon.SET_COMMAND:
|
||||
self.static_route_set_handler(key, data)
|
||||
elif op == swsscommon.DEL_COMMAND:
|
||||
self.static_route_del_handler(key, True)
|
||||
else:
|
||||
log_err("Invalid operation '%s' for key '%s'" % (op, key))
|
||||
|
||||
def bfd_state_split_key(self, key):
|
||||
"""
|
||||
Split key into table name, vrf name, interface name and peer ip.
|
||||
:param key: key to split
|
||||
:return: table name, vrf name, interface name and peer ip extracted from the key
|
||||
"""
|
||||
if key.count("|") < 2:
|
||||
return 'default', 'default', key
|
||||
else:
|
||||
return tuple(key.split('|'))
|
||||
|
||||
def append_to_srt_table_entry(self, srt_key, nh_info):
|
||||
entry = self.get_local_db(LOCAL_SRT_TABLE, srt_key)
|
||||
entry.add(nh_info)
|
||||
|
||||
def remove_from_srt_table_entry(self, srt_key, nh_info):
|
||||
entry = self.get_local_db(LOCAL_SRT_TABLE, srt_key)
|
||||
if nh_info in entry:
|
||||
entry.remove(nh_info)
|
||||
if len(entry) == 0:
|
||||
self.remove_from_local_db(LOCAL_SRT_TABLE, srt_key)
|
||||
|
||||
def set_static_route_into_appl_db(self, key, data):
|
||||
fvs = swsscommon.FieldValuePairs(list(data.items()))
|
||||
self.static_route_appl_tbl.set(key, fvs)
|
||||
log_debug("SRT_BFD: set static route to appl_db, key %s, data %s"%(key, str(data)))
|
||||
|
||||
def del_static_route_from_appl_db(self, key):
|
||||
self.static_route_appl_tbl.delete(key)
|
||||
|
||||
def reconstruct_static_route_config(self, original_config, reachable_nexthops):
|
||||
arg_list = lambda v: [x.strip() for x in v.split(',')] if len(v.strip()) != 0 else None
|
||||
bkh_list = arg_list(original_config['blackhole']) if 'blackhole' in original_config else None
|
||||
nh_list = arg_list(original_config['nexthop']) if 'nexthop' in original_config else None
|
||||
intf_list = arg_list(original_config['ifname']) if 'ifname' in original_config else None
|
||||
dist_list = arg_list(original_config['distance']) if 'distance' in original_config else None
|
||||
nh_vrf_list = arg_list(original_config['nexthop-vrf']) if 'nexthop-vrf' in original_config else None
|
||||
|
||||
bkh_candidate = ""
|
||||
nh_candidate = ""
|
||||
intf_candidate = ""
|
||||
dist_candidate = ""
|
||||
nh_vrf_candidate = ""
|
||||
|
||||
|
||||
for i in range(len(nh_list)):
|
||||
if (nh_vrf_list[i], nh_list[i]) in reachable_nexthops:
|
||||
bkh_candidate += "," + (bkh_list[i] if bkh_list else "")
|
||||
nh_candidate += "," + (nh_list[i] if nh_list else "")
|
||||
intf_candidate += "," + (intf_list[i] if intf_list else "")
|
||||
dist_candidate += "," + (dist_list[i] if dist_list else "")
|
||||
nh_vrf_candidate += "," + (nh_vrf_list[i] if nh_vrf_list else "")
|
||||
|
||||
new_config = dict()
|
||||
for key in original_config:
|
||||
if key == "bfd":
|
||||
continue
|
||||
if key == "bfd_nh_hold":
|
||||
continue
|
||||
if key == "blackhole":
|
||||
new_config[key] = bkh_candidate[1:]
|
||||
elif key == "nexthop":
|
||||
new_config[key] = nh_candidate[1:]
|
||||
elif key == "ifname":
|
||||
new_config[key] = intf_candidate[1:]
|
||||
elif key == "distance":
|
||||
new_config[key] = dist_candidate[1:]
|
||||
elif key == "nexthop-vrf":
|
||||
new_config[key] = nh_vrf_candidate[1:]
|
||||
else:
|
||||
new_config[key] = original_config[key]
|
||||
new_config["expiry"] = "false"
|
||||
|
||||
return new_config
|
||||
|
||||
|
||||
def bfd_state_set_handler(self, key, data):
|
||||
#key are diff in state db and appl_db,
|
||||
#intf is always default for multihop bfd
|
||||
vrf, intf, peer_ip = self.bfd_state_split_key(key)
|
||||
bfd_key = vrf + ":" + intf + ":" + peer_ip
|
||||
|
||||
#check if the BFD session is in local table
|
||||
bfd_session = self.get_local_db(LOCAL_BFD_TABLE, bfd_key)
|
||||
if len(bfd_session) == 0:
|
||||
return True
|
||||
|
||||
nh_key = vrf + "|" + peer_ip
|
||||
state = data['state'] if 'state' in data else "DOWN"
|
||||
log_info("bfd seesion %s state %s" %(bfd_key, state))
|
||||
|
||||
self.local_db[LOCAL_BFD_TABLE][bfd_key]["state"] = state
|
||||
|
||||
if state.upper() == "UP":
|
||||
for prefix in self.get_local_db(LOCAL_NEXTHOP_TABLE, nh_key):
|
||||
srt_key = prefix
|
||||
config_key = prefix
|
||||
#skip if the (vrf, peer_ip) is already in the nexthop list
|
||||
if (vrf, peer_ip) in self.get_local_db(LOCAL_SRT_TABLE, srt_key):
|
||||
continue
|
||||
self.append_to_srt_table_entry(srt_key, (vrf, peer_ip))
|
||||
config_data = self.get_local_db(LOCAL_CONFIG_TABLE, config_key)
|
||||
#exit "hold" state when any BFD session becomes UP
|
||||
config_data['bfd_nh_hold'] = "false"
|
||||
new_config = self.reconstruct_static_route_config(config_data, self.get_local_db(LOCAL_SRT_TABLE, srt_key))
|
||||
self.set_static_route_into_appl_db(srt_key.replace("|", ":"), new_config)
|
||||
|
||||
elif state.upper() == "DOWN":
|
||||
for prefix in self.get_local_db(LOCAL_NEXTHOP_TABLE, nh_key):
|
||||
srt_key = prefix
|
||||
config_key = prefix
|
||||
config_data = self.get_local_db(LOCAL_CONFIG_TABLE, config_key)
|
||||
#skip if the static route is in "hold" state
|
||||
if config_data['bfd_nh_hold'] == "true":
|
||||
continue
|
||||
self.remove_from_srt_table_entry(srt_key, (vrf, peer_ip))
|
||||
if len(self.get_local_db(LOCAL_SRT_TABLE, srt_key)) == 0:
|
||||
log_debug("SRT_BFD: bfd_state DOWN. nh_list is empty, delete static route from appl_db, key %s"%(srt_key.replace("|", ":")))
|
||||
self.del_static_route_from_appl_db(srt_key.replace("|", ":"))
|
||||
else:
|
||||
config_data = self.get_local_db(LOCAL_CONFIG_TABLE, config_key)
|
||||
new_config = self.reconstruct_static_route_config(config_data, self.get_local_db(LOCAL_SRT_TABLE, srt_key))
|
||||
self.set_static_route_into_appl_db(srt_key.replace("|", ":"), new_config)
|
||||
|
||||
|
||||
def bfd_state_del_handler(self, key):
|
||||
vrf, intf, peer_ip = self.bfd_state_split_key(key)
|
||||
bfd_key = vrf + ":" + intf + ":" + peer_ip
|
||||
|
||||
nh_key = vrf + "|" + peer_ip
|
||||
|
||||
for prefix in self.get_local_db(LOCAL_NEXTHOP_TABLE, nh_key):
|
||||
srt_key = prefix
|
||||
config_key = prefix
|
||||
self.remove_from_srt_table_entry(srt_key, (vrf, peer_ip))
|
||||
if len(self.get_local_db(LOCAL_SRT_TABLE, srt_key)) == 0:
|
||||
log_debug("SRT_BFD: bfd_state deletion. nh_list is empty, delete static route from appl_db, key %s"%(srt_key.replace("|", ":")))
|
||||
self.del_static_route_from_appl_db(srt_key.replace("|", ":"))
|
||||
else:
|
||||
config_data = self.get_local_db(LOCAL_CONFIG_TABLE, config_key)
|
||||
new_config = self.reconstruct_static_route_config(config_data, self.get_local_db(LOCAL_SRT_TABLE, srt_key))
|
||||
self.set_static_route_into_appl_db(srt_key.replace("|", ":"), new_config)
|
||||
|
||||
def bfd_state_callback(self, key, op, data):
|
||||
if op == swsscommon.SET_COMMAND:
|
||||
self.bfd_state_set_handler(key, data)
|
||||
elif op == swsscommon.DEL_COMMAND:
|
||||
self.bfd_state_del_handler(key)
|
||||
else:
|
||||
log_err("Invalid operation '%s' for key '%s'" % (op, key))
|
||||
|
||||
|
||||
def prepare_selector(self):
|
||||
interface_subscriber = swsscommon.SubscriberStateTable(self.config_db, INTERFACE_TABLE_NAME)
|
||||
portchannel_interface_subscriber = swsscommon.SubscriberStateTable(self.config_db, PORTCHANNEL_INTERFACE_TABLE_NAME)
|
||||
static_route_subscriber = swsscommon.SubscriberStateTable(self.config_db, STATIC_ROUTE_TABLE_NAME)
|
||||
bfd_state_subscriber = swsscommon.SubscriberStateTable(self.state_db, swsscommon.STATE_BFD_SESSION_TABLE_NAME)
|
||||
|
||||
self.selector.addSelectable(interface_subscriber)
|
||||
self.selector.addSelectable(portchannel_interface_subscriber)
|
||||
self.selector.addSelectable(static_route_subscriber)
|
||||
self.selector.addSelectable(bfd_state_subscriber)
|
||||
|
||||
self.subscribers.add(interface_subscriber)
|
||||
self.subscribers.add(portchannel_interface_subscriber)
|
||||
self.subscribers.add(static_route_subscriber)
|
||||
self.subscribers.add(bfd_state_subscriber)
|
||||
|
||||
self.callbacks[self.config_db.getDbId()][INTERFACE_TABLE_NAME].append(self.interface_callback)
|
||||
self.callbacks[self.config_db.getDbId()][PORTCHANNEL_INTERFACE_TABLE_NAME].append(self.interface_callback)
|
||||
self.callbacks[self.config_db.getDbId()][STATIC_ROUTE_TABLE_NAME].append(self.static_route_callback)
|
||||
self.callbacks[self.state_db.getDbId()][swsscommon.STATE_BFD_SESSION_TABLE_NAME].append(self.bfd_state_callback)
|
||||
|
||||
def run(self):
|
||||
self.prepare_selector()
|
||||
while g_run:
|
||||
state, _ = self.selector.select(self.SELECT_TIMEOUT)
|
||||
if state == self.selector.TIMEOUT:
|
||||
continue
|
||||
elif state == self.selector.ERROR:
|
||||
raise Exception("Received error from select")
|
||||
|
||||
if self.first_time:
|
||||
self.first_time = False
|
||||
self.reconciliation()
|
||||
|
||||
for sub in self.subscribers:
|
||||
while True:
|
||||
key, op, fvs = sub.pop()
|
||||
if len(key) == 0:
|
||||
break
|
||||
log_debug("Received message : '%s'" % str((key, op, fvs)))
|
||||
for callback in self.callbacks[sub.getDbConnector().getDbId()][sub.getTableName()]:
|
||||
callback(key, op, dict(fvs))
|
||||
|
||||
def do_work():
|
||||
sr_bfd = StaticRouteBfd()
|
||||
sr_bfd.run()
|
||||
|
||||
def main():
|
||||
rc = 0
|
||||
try:
|
||||
syslog.openlog('staticroutebfd')
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
do_work()
|
||||
except KeyboardInterrupt:
|
||||
syslog.syslog(syslog.LOG_NOTICE, "Keyboard interrupt")
|
||||
pass
|
||||
except RuntimeError as exc:
|
||||
syslog.syslog(syslog.LOG_CRIT, str(exc))
|
||||
rc = -2
|
||||
if g_debug:
|
||||
raise
|
||||
except Exception as exc:
|
||||
syslog.syslog(syslog.LOG_CRIT, "Got an exception %s: Traceback: %s" % (str(exc), traceback.format_exc()))
|
||||
rc = -1
|
||||
if g_debug:
|
||||
raise
|
||||
finally:
|
||||
syslog.closelog()
|
||||
try:
|
||||
sys.exit(rc)
|
||||
except SystemExit:
|
||||
os._exit(rc)
|
5
src/sonic-bgpcfgd/staticroutebfd/vars.py
Normal file
5
src/sonic-bgpcfgd/staticroutebfd/vars.py
Normal file
@ -0,0 +1,5 @@
|
||||
g_debug = True
|
||||
bfd_multihop = "true"
|
||||
bfd_rx_interval = "50"
|
||||
bfd_tx_interval = "50"
|
||||
bfd_multiplier = "3"
|
@ -604,7 +604,7 @@ def test_set_no_action(mocked_log_debug):
|
||||
True,
|
||||
[]
|
||||
)
|
||||
mocked_log_debug.assert_called_with("Nothing to update for static route default|10.1.1.0/24")
|
||||
mocked_log_debug.assert_called_with("CONFIG_DB Nothing to update for static route default|10.1.1.0/24")
|
||||
|
||||
@patch('bgpcfgd.managers_static_rt.log_debug')
|
||||
def test_del_no_action(mocked_log_debug):
|
||||
@ -616,7 +616,7 @@ def test_del_no_action(mocked_log_debug):
|
||||
True,
|
||||
[]
|
||||
)
|
||||
mocked_log_debug.assert_called_with("Nothing to update for static route default|10.1.1.0/24")
|
||||
mocked_log_debug.assert_called_with("CONFIG_DB Nothing to update for static route default|10.1.1.0/24")
|
||||
|
||||
def test_set_invalid_arg():
|
||||
mgr = constructor()
|
||||
@ -821,3 +821,109 @@ def test_set_tag_change():
|
||||
"ip route 10.1.0.0/24 10.0.0.57 tag 2",
|
||||
]
|
||||
)
|
||||
|
||||
def test_set_bfd_false():
|
||||
mgr = constructor()
|
||||
set_del_test(
|
||||
mgr,
|
||||
"SET",
|
||||
("10.1.0.0/24", {
|
||||
"bfd": "false",
|
||||
"nexthop": "PortChannel0001",
|
||||
}),
|
||||
True,
|
||||
[
|
||||
"ip route 10.1.0.0/24 PortChannel0001 tag 1",
|
||||
"route-map STATIC_ROUTE_FILTER permit 10",
|
||||
" match tag 1",
|
||||
"router bgp 65100",
|
||||
" address-family ipv4",
|
||||
" redistribute static route-map STATIC_ROUTE_FILTER",
|
||||
" address-family ipv6",
|
||||
" redistribute static route-map STATIC_ROUTE_FILTER"
|
||||
]
|
||||
)
|
||||
|
||||
set_del_test(
|
||||
mgr,
|
||||
"DEL",
|
||||
("10.1.0.0/24",),
|
||||
True,
|
||||
[
|
||||
"no ip route 10.1.0.0/24 PortChannel0001 tag 1",
|
||||
"router bgp 65100",
|
||||
" address-family ipv4",
|
||||
" no redistribute static route-map STATIC_ROUTE_FILTER",
|
||||
" address-family ipv6",
|
||||
" no redistribute static route-map STATIC_ROUTE_FILTER",
|
||||
"no route-map STATIC_ROUTE_FILTER"
|
||||
]
|
||||
)
|
||||
|
||||
def test_set_bfd_true():
|
||||
mgr = constructor()
|
||||
set_del_test(
|
||||
mgr,
|
||||
"SET",
|
||||
("10.1.0.0/24", {
|
||||
"bfd": "false",
|
||||
"nexthop": "PortChannel0001",
|
||||
}),
|
||||
True,
|
||||
[
|
||||
"ip route 10.1.0.0/24 PortChannel0001 tag 1",
|
||||
"route-map STATIC_ROUTE_FILTER permit 10",
|
||||
" match tag 1",
|
||||
"router bgp 65100",
|
||||
" address-family ipv4",
|
||||
" redistribute static route-map STATIC_ROUTE_FILTER",
|
||||
" address-family ipv6",
|
||||
" redistribute static route-map STATIC_ROUTE_FILTER"
|
||||
]
|
||||
)
|
||||
#do nothing for adding smae route second time
|
||||
set_del_test(
|
||||
mgr,
|
||||
"SET",
|
||||
("10.1.0.0/24", {
|
||||
"bfd": "false",
|
||||
"nexthop": "PortChannel0001",
|
||||
}),
|
||||
True,
|
||||
[
|
||||
]
|
||||
)
|
||||
#clear internal cache if bfd flag is true
|
||||
set_del_test(
|
||||
mgr,
|
||||
"SET",
|
||||
("10.1.0.0/24", {
|
||||
"bfd": "true",
|
||||
"nexthop": "PortChannel0001",
|
||||
}),
|
||||
True,
|
||||
[
|
||||
]
|
||||
)
|
||||
|
||||
#install the route becasue that cache was cleared above
|
||||
set_del_test(
|
||||
mgr,
|
||||
"SET",
|
||||
("10.1.0.0/24", {
|
||||
"bfd": "false",
|
||||
"nexthop": "PortChannel0001",
|
||||
}),
|
||||
True,
|
||||
[
|
||||
"ip route 10.1.0.0/24 PortChannel0001 tag 1",
|
||||
"route-map STATIC_ROUTE_FILTER permit 10",
|
||||
" match tag 1",
|
||||
"router bgp 65100",
|
||||
" address-family ipv4",
|
||||
" redistribute static route-map STATIC_ROUTE_FILTER",
|
||||
" address-family ipv6",
|
||||
" redistribute static route-map STATIC_ROUTE_FILTER"
|
||||
]
|
||||
)
|
||||
|
||||
|
516
src/sonic-bgpcfgd/tests/test_static_rt_bfd.py
Normal file
516
src/sonic-bgpcfgd/tests/test_static_rt_bfd.py
Normal file
@ -0,0 +1,516 @@
|
||||
from unittest.mock import patch
|
||||
#from unittest.mock import MagicMock, patch
|
||||
|
||||
from staticroutebfd.main import *
|
||||
from swsscommon import swsscommon
|
||||
|
||||
@patch('swsscommon.swsscommon.DBConnector.__init__')
|
||||
@patch('swsscommon.swsscommon.ProducerStateTable.__init__')
|
||||
@patch('swsscommon.swsscommon.Table.__init__')
|
||||
def constructor(mock_db, mock_producer, mock_tbl):
|
||||
mock_db.return_value = None
|
||||
mock_producer.return_value = None
|
||||
mock_tbl.return_value = None
|
||||
|
||||
srt_bfd = StaticRouteBfd()
|
||||
return srt_bfd
|
||||
|
||||
def set_del_test(dut, hdlr, op, args, e_bfd_dict, e_srt_dict):
|
||||
set_del_test.bfd_dict = {}
|
||||
set_del_test.srt_dict = {}
|
||||
|
||||
def bfd_app_set(key, data):
|
||||
set_del_test.bfd_dict["set_"+key] = data.copy()
|
||||
def bfd_app_del(key):
|
||||
set_del_test.bfd_dict["del_"+key] = {}
|
||||
def srt_app_set(key, data):
|
||||
set_del_test.srt_dict["set_"+key] = data.copy()
|
||||
def srt_app_del(key):
|
||||
set_del_test.srt_dict["del_"+key] = {}
|
||||
|
||||
def compare_dict(r, e):
|
||||
if len(r) == 0 and len(e) == 0:
|
||||
return True
|
||||
if len(r) != len(e):
|
||||
return False
|
||||
for k in e:
|
||||
if k not in r:
|
||||
return False
|
||||
if type(e[k]) is str:
|
||||
r_sort = "".join(sorted([x.strip() for x in r[k].split(',')]))
|
||||
e_sort = "".join(sorted([x.strip() for x in e[k].split(',')]))
|
||||
if r_sort != e_sort:
|
||||
return False
|
||||
if type(e[k]) is dict:
|
||||
ret = compare_dict(r[k], e[k])
|
||||
if not ret:
|
||||
return False
|
||||
return True
|
||||
|
||||
dut.set_bfd_session_into_appl_db = bfd_app_set
|
||||
dut.del_bfd_session_from_appl_db = bfd_app_del
|
||||
dut.set_static_route_into_appl_db = srt_app_set
|
||||
dut.del_static_route_from_appl_db = srt_app_del
|
||||
|
||||
if op == "SET":
|
||||
if hdlr == "bfd":
|
||||
dut.bfd_state_set_handler(*args)
|
||||
if hdlr == "srt":
|
||||
dut.static_route_set_handler(*args)
|
||||
if hdlr == "intf":
|
||||
dut.interface_set_handler(*args)
|
||||
elif op == "DEL":
|
||||
if hdlr == "bfd":
|
||||
dut.bfd_state_del_handler(*args)
|
||||
if hdlr == "srt":
|
||||
dut.static_route_del_handler(*args)
|
||||
if hdlr == "intf":
|
||||
dut.interface_del_handler(*args)
|
||||
else:
|
||||
assert False, "Wrong operation"
|
||||
|
||||
assert compare_dict(set_del_test.bfd_dict, e_bfd_dict)
|
||||
assert compare_dict(set_del_test.srt_dict, e_srt_dict)
|
||||
|
||||
def intf_setup(dut):
|
||||
set_del_test(dut, "intf",
|
||||
"SET",
|
||||
("if1|192.168.1.1/24", {}
|
||||
),
|
||||
{},
|
||||
{}
|
||||
)
|
||||
set_del_test(dut, "intf",
|
||||
"SET",
|
||||
("if2|192.168.2.1/24", {}
|
||||
),
|
||||
{},
|
||||
{}
|
||||
)
|
||||
set_del_test(dut, "intf",
|
||||
"SET",
|
||||
("if3|192.168.3.1/24", {}
|
||||
),
|
||||
{},
|
||||
{}
|
||||
)
|
||||
|
||||
def test_set_del():
|
||||
dut = constructor()
|
||||
intf_setup(dut)
|
||||
|
||||
#test #1
|
||||
set_del_test(dut, "srt",
|
||||
"SET",
|
||||
("2.2.2.0/24", {
|
||||
"bfd": "true",
|
||||
"nexthop": "192.168.1.2 , 192.168.2.2, 192.168.3.2",
|
||||
"ifname": "if1, if2, if3",
|
||||
}),
|
||||
{
|
||||
"set_default:default:192.168.1.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.1.1'},
|
||||
"set_default:default:192.168.2.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.2.1'},
|
||||
"set_default:default:192.168.3.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.3.1'}
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.1.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.1.2', 'ifname': 'if1', 'nexthop-vrf': 'default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.2.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2 ', 'ifname': 'if2,if1', 'nexthop-vrf': 'default,default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.3.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2,192.168.3.2 ', 'ifname': 'if2,if1,if3', 'nexthop-vrf': 'default,default,default', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
#test #2
|
||||
set_del_test(dut, "srt",
|
||||
"SET",
|
||||
("2.2.2.0/24", {
|
||||
"bfd": "true",
|
||||
"nexthop": "192.168.1.2 , 192.168.2.2",
|
||||
"ifname": "if1, if2",
|
||||
}),
|
||||
{
|
||||
"del_default:default:192.168.3.2" : {}
|
||||
},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2 ', 'ifname': 'if2,if1', 'nexthop-vrf': 'default,default', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
#test #3
|
||||
set_del_test(dut, "srt",
|
||||
"DEL",
|
||||
("2.2.2.0/24", {
|
||||
"bfd": "true",
|
||||
"nexthop": "192.168.1.2 , 192.168.2.2",
|
||||
"ifname": "if1, if2",
|
||||
}),
|
||||
{
|
||||
"del_default:default:192.168.1.2" : {},
|
||||
"del_default:default:192.168.2.2" : {}
|
||||
},
|
||||
{'del_default:2.2.2.0/24': {}}
|
||||
)
|
||||
|
||||
def test_bfd_del():
|
||||
dut = constructor()
|
||||
intf_setup(dut)
|
||||
|
||||
set_del_test(dut, "srt",
|
||||
"SET",
|
||||
("2.2.2.0/24", {
|
||||
"bfd": "true",
|
||||
"nexthop": "192.168.1.2 , 192.168.2.2, 192.168.3.2",
|
||||
"ifname": "if1, if2, if3",
|
||||
}),
|
||||
{
|
||||
"set_default:default:192.168.1.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.1.1'},
|
||||
"set_default:default:192.168.2.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.2.1'},
|
||||
"set_default:default:192.168.3.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.3.1'}
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.1.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.1.2', 'ifname': 'if1', 'nexthop-vrf': 'default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.2.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2 ', 'ifname': 'if2,if1', 'nexthop-vrf': 'default,default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.3.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2,192.168.3.2 ', 'ifname': 'if2,if1,if3', 'nexthop-vrf': 'default,default,default', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
#test bfd state del
|
||||
set_del_test(dut, "bfd",
|
||||
"DEL",
|
||||
({"192.168.2.2"}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.1.2,192.168.3.2 ', 'ifname': 'if1,if3', 'nexthop-vrf': 'default,default', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
def test_set_2routes():
|
||||
dut = constructor()
|
||||
intf_setup(dut)
|
||||
|
||||
#test #4
|
||||
set_del_test(dut, "srt",
|
||||
"SET",
|
||||
("2.2.2.0/24", {
|
||||
"bfd": "true",
|
||||
"nexthop": "192.168.1.2 , 192.168.2.2, 192.168.3.2",
|
||||
"ifname": "if1, if2, if3",
|
||||
}),
|
||||
{
|
||||
"set_default:default:192.168.1.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.1.1'},
|
||||
"set_default:default:192.168.2.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.2.1'},
|
||||
"set_default:default:192.168.3.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.3.1'}
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.1.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.1.2', 'ifname': 'if1', 'nexthop-vrf': 'default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.2.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2 ', 'ifname': 'if2,if1', 'nexthop-vrf': 'default,default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.3.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2,192.168.3.2 ', 'ifname': 'if2,if1,if3', 'nexthop-vrf': 'default,default,default', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
set_del_test(dut, "srt",
|
||||
"SET",
|
||||
("3.3.3.0/24", {
|
||||
"bfd": "true",
|
||||
"nexthop": "192.168.2.2",
|
||||
"ifname": "if2",
|
||||
}),
|
||||
{},
|
||||
{'set_default:3.3.3.0/24': {'nexthop': '192.168.2.2', 'ifname': 'if2', 'nexthop-vrf': 'default', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
#test #5
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.2.2", {
|
||||
"state": "Down"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.3.2,192.168.1.2 ', 'ifname': 'if3,if1', 'nexthop-vrf': 'default,default', 'expiry': 'false'}, 'del_default:3.3.3.0/24': {}}
|
||||
)
|
||||
|
||||
#test #6
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.2.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2,192.168.3.2 ', 'ifname': 'if2,if1,if3', 'nexthop-vrf': 'default,default,default', 'expiry': 'false'},
|
||||
'set_default:3.3.3.0/24': {'nexthop': '192.168.2.2', 'ifname': 'if2', 'nexthop-vrf': 'default', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
def test_set_bfd_change_hold():
|
||||
dut = constructor()
|
||||
intf_setup(dut)
|
||||
|
||||
#test #9 bfd: true -> false
|
||||
set_del_test(dut, "srt",
|
||||
"SET",
|
||||
("2.2.2.0/24", {
|
||||
"bfd": "true",
|
||||
"nexthop": "192.168.1.2 , 192.168.2.2, 192.168.3.2",
|
||||
"ifname": "if1, if2, if3",
|
||||
}),
|
||||
{
|
||||
"set_default:default:192.168.1.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.1.1'},
|
||||
"set_default:default:192.168.2.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.2.1'},
|
||||
"set_default:default:192.168.3.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.3.1'}
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.1.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.1.2', 'ifname': 'if1', 'nexthop-vrf': 'default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.2.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2 ', 'ifname': 'if2,if1', 'nexthop-vrf': 'default,default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.3.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2,192.168.3.2 ', 'ifname': 'if2,if1,if3', 'nexthop-vrf': 'default,default,default', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
set_del_test(dut, "srt",
|
||||
"SET",
|
||||
("2.2.2.0/24", {
|
||||
"bfd": "false",
|
||||
"nexthop": "192.168.1.2 , 192.168.2.2, 192.168.3.2",
|
||||
"ifname": "if1, if2, if3",
|
||||
}),
|
||||
{
|
||||
"del_default:default:192.168.1.2" : {},
|
||||
"del_default:default:192.168.2.2" : {},
|
||||
"del_default:default:192.168.3.2" : {}
|
||||
},
|
||||
{
|
||||
'del_default:2.2.2.0/24': {}
|
||||
}
|
||||
)
|
||||
return
|
||||
|
||||
#test #10 'bfd': false --> true, write original rout first
|
||||
set_del_test(dut, "srt",
|
||||
"SET",
|
||||
("2.2.2.0/24", {
|
||||
"bfd": "true",
|
||||
"nexthop": "192.168.1.2 , 192.168.2.2, 192.168.3.2",
|
||||
"ifname": "if1, if2, if3",
|
||||
}),
|
||||
{
|
||||
"set_default:default:192.168.1.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.1.1'},
|
||||
"set_default:default:192.168.2.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.2.1'},
|
||||
"set_default:default:192.168.3.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.3.1'}
|
||||
},
|
||||
{'set_default:2.2.2.0/24': {'bfd':'false', 'nexthop': '192.168.2.2,192.168.1.2,192.168.3.2 ', 'ifname': 'if2,if1,if3', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.1.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.1.2', 'ifname': 'if1', 'nexthop-vrf': 'default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.2.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2 ', 'ifname': 'if2,if1', 'nexthop-vrf': 'default,default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.3.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2,192.168.3.2 ', 'ifname': 'if2,if1,if3', 'nexthop-vrf': 'default,default,default', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
|
||||
def test_set_bfd_change_no_hold():
|
||||
dut = constructor()
|
||||
intf_setup(dut)
|
||||
|
||||
#setup runtime "bfd"="false" condition``
|
||||
set_del_test(dut, "srt",
|
||||
"SET",
|
||||
("2.2.2.0/24", {
|
||||
"bfd": "true",
|
||||
"nexthop": "192.168.1.2 , 192.168.2.2, 192.168.3.2",
|
||||
"ifname": "if1, if2, if3",
|
||||
}),
|
||||
{
|
||||
"set_default:default:192.168.1.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.1.1'},
|
||||
"set_default:default:192.168.2.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.2.1'},
|
||||
"set_default:default:192.168.3.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.3.1'}
|
||||
},
|
||||
{}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.1.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.1.2', 'ifname': 'if1', 'nexthop-vrf': 'default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.2.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2 ', 'ifname': 'if2,if1', 'nexthop-vrf': 'default,default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.3.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2,192.168.3.2 ', 'ifname': 'if2,if1,if3', 'nexthop-vrf': 'default,default,default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "srt",
|
||||
"SET",
|
||||
("3.3.3.0/24", {
|
||||
"bfd": "true",
|
||||
"nexthop": "192.168.2.2",
|
||||
"ifname": "if2",
|
||||
}),
|
||||
{},
|
||||
{'set_default:3.3.3.0/24': {'nexthop': '192.168.2.2', 'ifname': 'if2', 'nexthop-vrf': 'default', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
set_del_test(dut, "srt",
|
||||
"SET",
|
||||
("2.2.2.0/24", {
|
||||
"bfd": "false",
|
||||
"nexthop": "192.168.1.2 , 192.168.2.2, 192.168.3.2",
|
||||
"ifname": "if1, if2, if3",
|
||||
}),
|
||||
{
|
||||
"del_default:default:192.168.1.2" : {},
|
||||
"del_default:default:192.168.3.2" : {}
|
||||
},
|
||||
{
|
||||
'del_default:2.2.2.0/24': {}
|
||||
}
|
||||
)
|
||||
|
||||
#test #10 change 'bfd': false to true, because the bfd session "default:default:192.168.2.2" is up, so add that nexthop right after "bfd" change to "true"
|
||||
set_del_test(dut, "srt",
|
||||
"SET",
|
||||
("2.2.2.0/24", {
|
||||
"bfd": "true",
|
||||
"nexthop": "192.168.1.2 , 192.168.2.2, 192.168.3.2",
|
||||
"ifname": "if1, if2, if3",
|
||||
}),
|
||||
{
|
||||
"set_default:default:192.168.1.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.1.1'},
|
||||
"set_default:default:192.168.3.2" : {'multihop': 'true', 'rx_interval': '50', 'tx_interval': '50', 'multiplier': '3', 'local_addr': '192.168.3.1'}
|
||||
},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2', 'ifname': 'if2', 'nexthop-vrf': 'default', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.1.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2 ', 'ifname': 'if2,if1', 'nexthop-vrf': 'default,default', 'expiry': 'false'}}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.2.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{}
|
||||
)
|
||||
set_del_test(dut, "bfd",
|
||||
"SET",
|
||||
("192.168.3.2", {
|
||||
"state": "Up"
|
||||
}),
|
||||
{},
|
||||
{'set_default:2.2.2.0/24': {'nexthop': '192.168.2.2,192.168.1.2,192.168.3.2 ', 'ifname': 'if2,if1,if3', 'nexthop-vrf': 'default,default,default', 'expiry': 'false'}}
|
||||
)
|
||||
|
||||
|
Reference in New Issue
Block a user