Revert "[bgp] Add 'allow list' manager feature (#5309)"

This reverts commit 6eed0820c8.
This commit is contained in:
Guohan Lu 2020-09-28 22:00:29 -07:00
parent e3f8159606
commit e412338743
21 changed files with 22 additions and 1515 deletions

View File

@ -24,7 +24,7 @@
{% if CONFIG_DB__DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %}
neighbor PEER_V6 allowas-in 1
neighbor PEER_V6_INT allowas-in 1
{% endif %}
{% endif %}
{% if CONFIG_DB__DEVICE_METADATA['localhost']['sub_role'] == 'BackEnd' %}
neighbor PEER_V6_INT route-reflector-client
{% endif %}

View File

@ -3,33 +3,6 @@
!
!
!
{% if constants.bgp.allow_list is defined and constants.bgp.allow_list.enabled is defined and constants.bgp.allow_list.enabled %}
{% if constants.bgp.allow_list.default_action is defined and constants.bgp.allow_list.default_action.strip() == 'deny' %}
route-map ALLOW_LIST_DEPLOYMENT_ID_0_V4 permit 65535
set community no-export additive
!
route-map ALLOW_LIST_DEPLOYMENT_ID_0_V6 permit 65535
set community no-export additive
{% else %}
route-map ALLOW_LIST_DEPLOYMENT_ID_0_V4 permit 65535
set community {{ constants.bgp.allow_list.drop_community }} additive
!
route-map ALLOW_LIST_DEPLOYMENT_ID_0_V6 permit 65535
set community {{ constants.bgp.allow_list.drop_community }} additive
{% endif %}
!
route-map FROM_BGP_PEER_V4 permit 2
call ALLOW_LIST_DEPLOYMENT_ID_0_V4
on-match next
!
route-map FROM_BGP_PEER_V6 permit 2
call ALLOW_LIST_DEPLOYMENT_ID_0_V6
on-match next
!
{% endif %}
!
!
!
route-map FROM_BGP_PEER_V4 permit 100
!
route-map TO_BGP_PEER_V4 permit 100

View File

@ -18,18 +18,6 @@ constants:
enabled: true
ipv4: 64
ipv6: 64
allow_list:
enabled: true
default_action: "permit" # or "deny"
drop_community: 5060:12345 # value of the community to identify a prefix to drop. Make sense only with allow_list_default_action equal to 'permit'
default_pl_rules:
v4:
- "deny 0.0.0.0/0 le 17"
- "permit 127.0.0.1/32"
v6:
- "deny 0::/0 le 59"
- "deny 0::/0 ge 65"
- "permit fe80::/64"
peers:
general: # peer_type
db_table: "BGP_NEIGHBOR"

View File

@ -6,7 +6,6 @@ $(SONIC_BGPCFGD)_SRC_PATH = $(SRC_PATH)/sonic-bgpcfgd
# of sonic-config-engine and bgpcfgd explicitly calls sonic-cfggen
# as part of its unit tests.
# TODO: Refactor unit tests so that these dependencies are not needed
$(SONIC_BGPCFGD)_DEPENDS += $(SONIC_PY_COMMON_PY2)
$(SONIC_BGPCFGD)_DEBS_DEPENDS += $(LIBSWSSCOMMON) $(PYTHON_SWSSCOMMON)
$(SONIC_BGPCFGD)_DEPENDS += $(SWSSSDK_PY2) $(SONIC_PY_COMMON_PY2)
$(SONIC_BGPCFGD)_PYTHON_VERSION = 2
SONIC_PYTHON_WHEELS += $(SONIC_BGPCFGD)

View File

@ -6,4 +6,3 @@ app/*.pyc
tests/*.pyc
tests/__pycache__/
.idea
.coverage

View File

@ -1,632 +0,0 @@
"""
Implementation of "allow-list" feature
"""
import re
from app.log import log_debug, log_info, log_err, log_warn
from app.template import TemplateFabric
from app.manager import Manager
from app.util import run_command
class BGPAllowListMgr(Manager):
""" This class initialize "AllowList" settings """
ALLOW_ADDRESS_PL_NAME_TMPL = "ALLOW_ADDRESS_%d_%s" # template for a name for the ALLOW_ADDRESS prefix-list ???
EMPTY_COMMUNITY = "empty"
PL_NAME_TMPL = "PL_ALLOW_LIST_DEPLOYMENT_ID_%d_COMMUNITY_%s_V%s"
COMMUNITY_NAME_TMPL = "COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_%d_COMMUNITY_%s"
RM_NAME_TMPL = "ALLOW_LIST_DEPLOYMENT_ID_%d_V%s"
ROUTE_MAP_ENTRY_WITH_COMMUNITY_START = 10
ROUTE_MAP_ENTRY_WITH_COMMUNITY_END = 29990
ROUTE_MAP_ENTRY_WITHOUT_COMMUNITY_START = 30000
ROUTE_MAP_ENTRY_WITHOUT_COMMUNITY_END = 65530
V4 = "v4" # constant for af enum: V4
V6 = "v6" # constant for af enum: V6
def __init__(self, common_objs, db, table):
"""
Initialize the object
:param common_objs: common object dictionary
:param db: name of the db
:param table: name of the table in the db
"""
super(BGPAllowListMgr, self).__init__(
common_objs,
[],
db,
table,
)
self.cfg_mgr = common_objs["cfg_mgr"]
self.constants = common_objs["constants"]
self.key_re = re.compile(r"^DEPLOYMENT_ID\|\d+\|\S+$|^DEPLOYMENT_ID\|\d+$")
self.enabled = self.__get_enabled()
self.__load_constant_lists()
def set_handler(self, key, data):
"""
Manager method which runs on receiving 'SET' message
:param key: ket of the 'SET' message
:param data: data of the 'SET' message
:return: True if the message was executed, False - the message should be postponed.
"""
if not self.enabled:
log_warn("BGPAllowListMgr::Received 'SET' command, but this feature is disabled in constants")
return True
if not self.__set_handler_validate(key, data):
return True
key = key.replace("DEPLOYMENT_ID|", "")
deployment_id, community_value = key.split('|', 1) if '|' in key else (key, BGPAllowListMgr.EMPTY_COMMUNITY)
deployment_id = int(deployment_id)
prefixes_v4 = []
prefixes_v6 = []
if "prefixes_v4" in data:
prefixes_v4 = str(data['prefixes_v4']).split(",")
if "prefixes_v6" in data:
prefixes_v6 = str(data['prefixes_v6']).split(",")
self.__update_policy(deployment_id, community_value, prefixes_v4, prefixes_v6)
return True
def __set_handler_validate(self, key, data):
"""
Validate parameters of a "Set" message
:param key: ket of the 'SET' message
:param data: data of the 'SET' message
:return: True if parameters are valid, False if parameters are invalid
"""
if data is None:
log_err("BGPAllowListMgr::Received BGP ALLOWED 'SET' message without data")
return False
if not self.key_re.match(key):
log_err("BGPAllowListMgr::Received BGP ALLOWED 'SET' message with invalid key: '%s'" % key)
return False
prefixes_v4 = []
prefixes_v6 = []
if "prefixes_v4" in data:
prefixes_v4 = str(data["prefixes_v4"]).split(",")
if not all(TemplateFabric.is_ipv4(prefix) for prefix in prefixes_v4):
arguments = "prefixes_v4", str(data["prefixes_v4"])
log_err("BGPAllowListMgr::Received BGP ALLOWED 'SET' message with invalid input[%s]:'%s'" % arguments)
return False
if "prefixes_v6" in data:
prefixes_v6 = str(data["prefixes_v6"]).split(",")
if not all(TemplateFabric.is_ipv6(prefix) for prefix in prefixes_v6):
arguments = "prefixes_v6", str(data["prefixes_v6"])
log_err("BGPAllowListMgr::Received BGP ALLOWED 'SET' message with invalid input[%s]:'%s'" % arguments)
return False
if not prefixes_v4 and not prefixes_v6:
log_err("BGPAllowListMgr::Received BGP ALLOWED 'SET' message with no prefixes specified: %s" % str(data))
return False
return True
def del_handler(self, key):
"""
Manager method which runs on "DEL" message
:param key: a key of "DEL" message
"""
if not self.enabled:
log_warn("BGPAllowListMgr::Received 'DEL' command, but this feature is disabled in constants")
return
if not self.__del_handler_validate(key):
return
key = key.replace('DEPLOYMENT_ID|', '')
deployment_id, community = key.split('|', 1) if '|' in key else (key, BGPAllowListMgr.EMPTY_COMMUNITY)
deployment_id = int(deployment_id)
self.__remove_policy(deployment_id, community)
def __del_handler_validate(self, key):
"""
Validate "DEL" method parameters
:param key: a key of "DEL" message
:return: True if parameters are valid, False if parameters are invalid
"""
if not self.key_re.match(key):
log_err("BGPAllowListMgr::Received BGP ALLOWED 'DEL' message with invalid key: '$s'" % key)
return False
return True
def __update_policy(self, deployment_id, community_value, prefixes_v4, prefixes_v6):
"""
Update "allow list" policy with parameters
:param deployment_id: deployment id which policy will be changed
:param community_value: community value to match for the updated policy
:param prefixes_v4: a list of v4 prefixes for the updated policy
:param prefixes_v6: a list of v6 prefixes for the updated policy
"""
# update all related entries with the information
info = deployment_id, community_value, str(prefixes_v4), str(prefixes_v6)
msg = "BGPAllowListMgr::Updating 'Allow list' policy."
msg += " deployment_id '%s'. community: '%s'"
msg += " prefix_v4 '%s'. prefix_v6: '%s'"
log_info(msg % info)
names = self.__generate_names(deployment_id, community_value)
self.cfg_mgr.update()
cmds = []
cmds += self.__update_prefix_list(self.V4, names['pl_v4'], prefixes_v4)
cmds += self.__update_prefix_list(self.V6, names['pl_v6'], prefixes_v6)
cmds += self.__update_community(names['community'], community_value)
cmds += self.__update_allow_route_map_entry(self.V4, names['pl_v4'], names['community'], names['rm_v4'])
cmds += self.__update_allow_route_map_entry(self.V6, names['pl_v6'], names['community'], names['rm_v6'])
if cmds:
rc = self.cfg_mgr.push_list(cmds)
rc = rc and self.__restart_peers(deployment_id)
log_debug("BGPAllowListMgr::__update_policy. The peers were updated: rc=%s" % rc)
else:
log_debug("BGPAllowListMgr::__update_policy. Nothing to update")
log_info("BGPAllowListMgr::Done")
def __remove_policy(self, deployment_id, community_value):
"""
Remove "allow list" policy for given deployment_id and community_value
:param deployment_id: deployment id which policy will be removed
:param community_value: community value to match for the removed policy
"""
# remove all related entries from the configuration
# put default rule to the route-map
info = deployment_id, community_value
msg = "BGPAllowListMgr::Removing 'Allow list' policy."
msg += " deployment_id '%s'. community: '%s'"
log_info(msg % info)
names = self.__generate_names(deployment_id, community_value)
self.cfg_mgr.update()
cmds = []
cmds += self.__remove_allow_route_map_entry(self.V4, names['pl_v4'], names['community'], names['rm_v4'])
cmds += self.__remove_allow_route_map_entry(self.V6, names['pl_v6'], names['community'], names['rm_v6'])
cmds += self.__remove_prefix_list(self.V4, names['pl_v4'])
cmds += self.__remove_prefix_list(self.V6, names['pl_v6'])
cmds += self.__remove_community(names['community'])
if cmds:
rc = self.cfg_mgr.push_list(cmds)
rc = rc and self.__restart_peers(deployment_id)
log_debug("BGPAllowListMgr::__remove_policy. 'Allow list' policy was removed. rc:%s" % rc)
else:
log_debug("BGPAllowListMgr::__remove_policy. Nothing to remove")
log_info('BGPAllowListMgr::Done')
@staticmethod
def __generate_names(deployment_id, community_value):
"""
Generate prefix-list names for a given peer_ip and community value
:param deployment_id: deployment_id for which we're going to filter prefixes
:param community_value: community, which we want to use to filter prefixes
:return: a dictionary with names
"""
if community_value == BGPAllowListMgr.EMPTY_COMMUNITY:
community_name = BGPAllowListMgr.EMPTY_COMMUNITY
else:
community_name = BGPAllowListMgr.COMMUNITY_NAME_TMPL % (deployment_id, community_value)
names = {
"pl_v4": BGPAllowListMgr.PL_NAME_TMPL % (deployment_id, community_value, '4'),
"pl_v6": BGPAllowListMgr.PL_NAME_TMPL % (deployment_id, community_value, '6'),
"rm_v4": BGPAllowListMgr.RM_NAME_TMPL % (deployment_id, '4'),
"rm_v6": BGPAllowListMgr.RM_NAME_TMPL % (deployment_id, '6'),
"community": community_name,
}
arguments = deployment_id, community_value, str(names)
log_debug("BGPAllowListMgr::__generate_names. deployment_id: %d, community: %s. names: %s" % arguments)
return names
def __update_prefix_list(self, af, pl_name, allow_list):
"""
Create or update a prefix-list with name pl_name.
:param af: "v4" to create ipv4 prefix-list, "v6" to create ipv6 prefix-list
:param pl_name: prefix-list name
:param allow_list: prefix-list entries
:return: True if updating was successful, False otherwise
"""
assert af == self.V4 or af == self.V6
constant_list = self.__get_constant_list(af)
allow_list = self.__to_prefix_list(allow_list)
log_debug("BGPAllowListMgr::__update_prefix_list. af='%s' prefix-list name=%s" % (af, pl_name))
exist, correct = self.__is_prefix_list_valid(af, pl_name, allow_list, constant_list)
if correct:
log_debug("BGPAllowListMgr::__update_prefix_list. the prefix-list '%s' exists and correct" % pl_name)
return []
family = self.__af_to_family(af)
cmds = []
seq_no = 10
if exist:
cmds.append('no %s prefix-list %s' % (family, pl_name))
for entry in constant_list + allow_list:
cmds.append('%s prefix-list %s seq %d %s' % (family, pl_name, seq_no, entry))
seq_no += 10
return cmds
def __remove_prefix_list(self, af, pl_name):
"""
Remove prefix-list in the address-family af.
:param af: "v4" to create ipv4 prefix-list, "v6" to create ipv6 prefix-list
:param pl_name: list of prefix-list names
:return: True if operation was successful, False otherwise
"""
assert af == self.V4 or af == self.V6
log_debug("BGPAllowListMgr::__remove_prefix_lists. af='%s' pl_names='%s'" % (af, pl_name))
exist, _ = self.__is_prefix_list_valid(af, pl_name, [], [])
if not exist:
log_debug("BGPAllowListMgr::__remove_prefix_lists: prefix_list '%s' not found" % pl_name)
return []
family = self.__af_to_family(af)
return ["no %s prefix-list %s" % (family, pl_name)]
def __is_prefix_list_valid(self, af, pl_name, allow_list, constant_list):
"""
Check that a prefix list exists and it has valid entries
:param af: address family of the checked prefix-list
:param pl_name: prefix-list name
:param allow_list: a prefix-list which must be a part of the valid prefix list
:param constant_list: a constant list which must be on top of each "allow" prefix list on the device
:return: a tuple. The first element of the tuple has True if the prefix-list exists, False otherwise,
The second element of the tuple has True if the prefix-list contains correct entries, False if not
"""
assert af == self.V4 or af == self.V6
family = self.__af_to_family(af)
match_string = '%s prefix-list %s seq ' % (family, pl_name)
conf = self.cfg_mgr.get_text()
if not any(line.strip().startswith(match_string) for line in conf):
return False, False # if the prefix list is not exists, it is not correct
constant_set = set(constant_list)
allow_set = set(allow_list)
for line in conf:
if line.startswith(match_string):
found = line[len(match_string):].strip().split(' ')
rule = " ".join(found[1:])
if rule in constant_set:
constant_set.discard(rule)
elif rule in allow_set:
if constant_set:
return True, False # Not everything from constant set is presented
else:
allow_set.discard(rule)
return True, len(allow_set) == 0 # allow_set should be presented all
def __update_community(self, community_name, community_value):
"""
Update community for a peer
:param community_name: name of the community to update
:param community_value: community value for the peer
:return: True if operation was successful, False otherwise
"""
log_debug("BGPAllowListMgr::__update_community. community_name='%s' community='%s'" % (community_name, community_value))
if community_value == self.EMPTY_COMMUNITY: # we don't need to do anything for EMPTY community
log_debug("BGPAllowListMgr::__update_community. Empty community. exiting")
return []
cmds = []
exists, found_community_value = self.__is_community_presented(community_name)
if exists:
if community_value == found_community_value:
log_debug("BGPAllowListMgr::__update_community. community '%s' is already presented" % community_name)
return []
else:
msg = "BGPAllowListMgr::__update_community. "
msg += "community '%s' is already presented, but community value should be updated" % community_name
log_debug(msg)
cmds.append("no bgp community-list standard %s" % community_name)
cmds.append('bgp community-list standard %s permit %s' % (community_name, community_value))
return cmds
def __remove_community(self, community_name):
"""
Remove community for a peer
:param community_name: community value for the peer
:return: True if operation was successful, False otherwise
"""
log_debug("BGPAllowListMgr::__remove_community. community='%s'" % community_name)
if community_name == self.EMPTY_COMMUNITY: # we don't need to do anything for EMPTY community
log_debug("BGPAllowListMgr::__remove_community. There is nothing to remove in empty community")
return []
exists, _ = self.__is_community_presented(community_name)
if not exists:
log_debug("BGPAllowListMgr::__remove_community. Community is already removed.")
return []
return ['no bgp community-list standard %s' % community_name]
def __is_community_presented(self, community_name):
"""
Return True if community for the peer_ip exists
:param community_name: community value for the peer
:return: A tuple. First element: True if operation was successful, False otherwise
Second element: community value if the first element is True no value otherwise
"""
log_debug("BGPAllowListMgr::__is_community_presented. community='%s'" % community_name)
match_string = 'bgp community-list standard %s permit ' % community_name
conf = self.cfg_mgr.get_text()
found = [line.strip() for line in conf if line.strip().startswith(match_string)]
if not found:
return False, None
community_value = found[0].replace(match_string, '')
return True, community_value
def __update_allow_route_map_entry(self, af, allow_address_pl_name, community_name, route_map_name):
"""
Add or update a "Allow address" route-map entry with the parameters
:param af: "v4" to create ipv4 prefix-list, "v6" to create ipv6 prefix-list
:return: True if operation was successful, False otherwise
"""
assert af == self.V4 or af == self.V6
info = af, route_map_name, allow_address_pl_name, community_name
log_debug("BGPAllowListMgr::__update_allow_route_map_entry. af='%s' Allow rm='%s' pl='%s' cl='%s'" % info)
entries = self.__parse_allow_route_map_entries(af, route_map_name)
found, _ = self.__find_route_map_entry(entries, allow_address_pl_name, community_name)
if found:
log_debug("BGPAllowListMgr::__update_allow_route_map_entry. route-map='%s' is already found" % route_map_name)
return []
seq_number = self.__find_next_seq_number(entries.keys(), community_name != self.EMPTY_COMMUNITY, route_map_name)
info = af, seq_number, allow_address_pl_name, community_name
out = "af='%s' seqno='%d' Allow pl='%s' cl='%s'" % info
log_debug("BGPAllowListMgr::__update_allow_route_map_entry. %s" % out)
ip_version = "" if af == self.V4 else "v6"
cmds = [
'route-map %s permit %d' % (route_map_name, seq_number),
' match ip%s address prefix-list %s' % (ip_version, allow_address_pl_name)
]
if not community_name.endswith(self.EMPTY_COMMUNITY):
cmds.append(" match community %s" % community_name)
return cmds
def __remove_allow_route_map_entry(self, af, allow_address_pl_name, community_name, route_map_name):
"""
Add or update a "Allow address" route-map entry with the parameters
:param af: "v4" to create ipv4 prefix-list, "v6" to create ipv6 prefix-list
:return: True if operation was successful, False otherwise
"""
assert af == self.V4 or af == self.V6
info = af, route_map_name, allow_address_pl_name, community_name
log_debug("BGPAllowListMgr::__update_allow_route_map_entry. af='%s' Allow rm='%s' pl='%s' cl='%s'" % info)
entries = self.__parse_allow_route_map_entries(af, route_map_name)
found, seq_number = self.__find_route_map_entry(entries, allow_address_pl_name, community_name)
if not found:
log_debug("BGPAllowListMgr::__update_allow_route_map_entry. Not found route-map '%s' entry" % allow_address_pl_name)
return []
return ['no route-map %s permit %d' % (route_map_name, seq_number)]
@staticmethod
def __find_route_map_entry(entries, allow_address_pl_name, community_name):
"""
Find route-map entry with given allow_address prefix list name and community name in the parsed route-map.
:param entries: entries of parsed route-map
:param allow_address_pl_name: name of the "allow address" prefix-list
:param community_name: name of the "allow address" community name
:return: a tuple. The first element of the tuple is True, if the route-map entry was found, False otherwise.
The second element of the tuple has a sequence number of the entry.
"""
for sequence_number, values in entries.items():
if sequence_number == 65535:
continue
allow_list_presented = values['pl_allow_list'] == allow_address_pl_name
community_presented = values['community'] == community_name
if allow_list_presented and community_presented:
log_debug("BGPAllowListMgr::__find_route_map_entry. found route-map '%s' entry" % allow_address_pl_name)
return True, sequence_number
return False, None
def __parse_allow_route_map_entries(self, af, route_map_name):
"""
Parse "Allow list" route-map entries.
:param af: "v4" to create ipv4 prefix-list, "v6" to create ipv6 prefix-list
:return: A tuple, First element: True if operation was successful, False otherwise
Second element: list of object with parsed route-map entries
"""
assert af == self.V4 or af == self.V6
log_debug("BGPAllowListMgr::__parse_allow_route_map_entries. af='%s', rm='%s'" % (af, route_map_name))
match_string = 'route-map %s permit ' % route_map_name
entries = {}
inside_route_map = False
route_map_seq_number = None
pl_allow_list_name = None
community_name = self.EMPTY_COMMUNITY
if af == self.V4:
match_pl_allow_list = 'match ip address prefix-list '
else: # self.V6
match_pl_allow_list = 'match ipv6 address prefix-list '
match_community = 'match community '
conf = self.cfg_mgr.get_text()
for line in conf + [""]:
if inside_route_map:
if line.strip().startswith(match_pl_allow_list):
pl_allow_list_name = line.strip()[len(match_pl_allow_list):]
continue
elif line.strip().startswith(match_community):
community_name = line.strip()[len(match_community):]
continue
else:
if pl_allow_list_name is not None:
entries[route_map_seq_number] = {
'pl_allow_list': pl_allow_list_name,
'community': community_name,
}
else:
if route_map_seq_number != 65535:
log_warn("BGPAllowListMgr::Found incomplete route-map '%s' entry. seq_no=%d" % (route_map_name, route_map_seq_number))
inside_route_map = False
pl_allow_list_name = None
community_name = self.EMPTY_COMMUNITY
route_map_seq_number = None
if line.startswith(match_string):
found = line[len(match_string):]
assert found.isdigit()
route_map_seq_number = int(found)
inside_route_map = True
return entries
@staticmethod
def __find_next_seq_number(seq_numbers, has_community, route_map_name):
"""
Find a next available "Allow list" route-map entry number
:param seq_numbers: a list of already used sequence numbers
:param has_community: True, if the route-map entry has community
:return: next available route-map sequence number
"""
used_sequence_numbers = set(seq_numbers)
sequence_number = None
if has_community: # put entries without communities after 29999
start_seq = BGPAllowListMgr.ROUTE_MAP_ENTRY_WITH_COMMUNITY_START
end_seq = BGPAllowListMgr.ROUTE_MAP_ENTRY_WITH_COMMUNITY_END
else:
start_seq = BGPAllowListMgr.ROUTE_MAP_ENTRY_WITHOUT_COMMUNITY_START
end_seq = BGPAllowListMgr.ROUTE_MAP_ENTRY_WITHOUT_COMMUNITY_END
for i in range(start_seq, end_seq, 10):
if i not in used_sequence_numbers:
sequence_number = i
break
if sequence_number is None:
raise RuntimeError("No free sequence numbers for '%s'" % route_map_name)
info = sequence_number, "yes" if has_community else "no"
log_debug("BGPAllowListMgr::__find_next_seq_number '%d' has_community='%s'" % info)
return sequence_number
def __extract_peer_group_names(self):
"""
Extract names of all peer-groups defined in the config
:return: list of peer-group names
"""
# Find all peer-groups entries
re_peer_group = re.compile(r'^\s*neighbor (\S+) peer-group$')
peer_groups = []
for line in self.cfg_mgr.get_text():
result = re_peer_group.match(line)
if result:
peer_groups.append(result.group(1))
return peer_groups
def __get_peer_group_to_route_map(self, peer_groups):
"""
Extract names of route-maps which is connected to peer-groups defines as peer_groups
:peer_groups: a list of peer-group names
:return: dictionary where key is a peer-group, value is a route-map name which is defined as route-map in
for the peer_group.
"""
pg_2_rm = {}
for pg in peer_groups:
re_peer_group_rm = re.compile(r'^\s*neighbor %s route-map (\S+) in$' % pg)
for line in self.cfg_mgr.get_text():
result = re_peer_group_rm.match(line)
if result:
pg_2_rm[pg] = result.group(1)
break
return pg_2_rm
def __get_route_map_calls(self, rms):
"""
Find mapping between route-maps and route-map call names, defined for the route-maps
:rms: a set with route-map names
:return: a dictionary: key - name of a route-map, value - name of a route-map call defined for the route-map
"""
rm_2_call = {}
re_rm = re.compile(r'^route-map (\S+) permit \d+$')
re_call = re.compile(r'^\s*call (\S+)$')
inside_name = None
for line in self.cfg_mgr.get_text():
if inside_name:
inside_result = re_call.match(line)
if inside_result:
rm_2_call[inside_name] = inside_result.group(1)
inside_name = None
continue
result = re_rm.match(line)
if not result:
continue
inside_name = None
if result.group(1) not in rms:
continue
inside_name = result.group(1)
return rm_2_call
def __get_peer_group_to_restart(self, deployment_id, pg_2_rm, rm_2_call):
"""
Get peer_groups which are assigned to deployment_id
:deployment_id: deployment_id number
:pg_2_rm: a dictionary where key is a peer-group, value is a route-map name which is defined as route-map in
for the peer_group.
:rm_2_call: a dictionary: key - name of a route-map, value - name of a route-map call defined for the route-map
"""
ret = set()
target_allow_list_prefix = 'ALLOW_LIST_DEPLOYMENT_ID_%d_V' % deployment_id
for peer_group, route_map in pg_2_rm.items():
if route_map in rm_2_call:
if rm_2_call[route_map].startswith(target_allow_list_prefix):
ret.add(peer_group)
return list(ret)
def __find_peer_group_by_deployment_id(self, deployment_id):
"""
Deduce peer-group names which are connected to devices with requested deployment_id
:param deployment_id: deployment_id number
:return: a list of peer-groups which a used by devices with requested deployment_id number
"""
self.cfg_mgr.update()
peer_groups = self.__extract_peer_group_names()
pg_2_rm = self.__get_peer_group_to_route_map(peer_groups)
rm_2_call = self.__get_route_map_calls(set(pg_2_rm.values()))
ret = self.__get_peer_group_to_restart(deployment_id, pg_2_rm, rm_2_call)
return list(ret)
def __restart_peers(self, deployment_id):
"""
Restart peer-groups with requested deployment_id
:param deployment_id: deployment_id number
"""
log_info("BGPAllowListMgr::Restart peers with deployment_id=%d" % deployment_id)
peer_groups = self.__find_peer_group_by_deployment_id(deployment_id)
rv = True
if peer_groups:
for peer_group in peer_groups:
no_error, _, _ = run_command(["vtysh", "-c", "clear bgp peer-group %s soft in" % peer_group])
rv = no_error == 0 and rv
else:
no_error, _, _ = run_command(["vtysh", "-c", "clear bgp * soft in"])
rv = no_error == 0
return rv
def __get_enabled(self):
"""
Load enable/disabled property from constants
:return: True if enabled, False otherwise
"""
return 'bgp' in self.constants \
and 'allow_list' in self.constants["bgp"] \
and "enabled" in self.constants["bgp"]["allow_list"] \
and self.constants["bgp"]["allow_list"]["enabled"]
def __load_constant_lists(self):
"""
Load default prefix-list entries from constants.yml file
"""
if 'bgp' in self.constants and 'allow_list' in self.constants["bgp"] \
and "default_pl_rules" in self.constants["bgp"]["allow_list"]:
obj = self.constants["bgp"]["allow_list"]["default_pl_rules"]
if "v4" in obj:
self.constants_v4 = obj["v4"]
else:
self.constants_v4 = []
if "v6" in obj:
self.constants_v6 = obj["v6"]
else:
self.constants_v6 = []
def __get_constant_list(self, af):
"""
Return loaded default prefix-list entries bases on address family
:param af: address family
:return: default prefix-list entries
"""
if af == self.V4:
return self.constants_v4
else:
return self.constants_v6
@staticmethod
def __to_prefix_list(allow_list):
"""
Convert "allow list" prefix list, to a prefix-list rules
:param allow_list: "allow list" prefix list
:return: prefix-list rules
"""
return ["permit %s ge %d" % (prefix, int(prefix.split("/")[1])+1) for prefix in allow_list]
def __af_to_family(self, af):
"""
Convert address family into prefix list family
:param af: address family
:return: prefix list ip family
"""
return 'ip' if af == self.V4 else 'ipv6'

View File

@ -10,33 +10,19 @@ class ConfigMgr(object):
""" The class represents frr configuration """
def __init__(self):
self.current_config = None
self.current_config_raw = None
def reset(self):
""" Reset stored config """
self.current_config = None
self.current_config_raw = None
def update(self):
""" Read current config from FRR """
self.current_config = None
self.current_config_raw = None
ret_code, out, err = run_command(["vtysh", "-c", "show running-config"])
if ret_code != 0:
# FIXME: should we throw exception here?
log_crit("can't update running config: rc=%d out='%s' err='%s'" % (ret_code, out, err))
return
text = []
for line in out.split('\n'):
if line.lstrip().startswith('!'):
continue
text.append(line)
text += [" "] # Add empty line to have something to work on, if there is no text
self.current_config_raw = text
self.current_config = self.to_canonical(out) # FIXME: use test as an input
def push_list(self, cmdlist):
return self.push("\n".join(cmdlist))
self.current_config = self.to_canonical(out)
def push(self, cmd):
"""
@ -65,12 +51,8 @@ class ConfigMgr(object):
log_err("ConfigMgr::push(): can't push configuration '%s', rc='%d', stdout='%s', stderr='%s'" % err_tuple)
if ret_code == 0:
self.current_config = None # invalidate config
self.current_config_raw = None
return ret_code == 0
def get_text(self):
return self.current_config_raw
@staticmethod
def to_canonical(raw_config):
"""

View File

@ -1,159 +0,0 @@
from collections import defaultdict
from app.log import log_err
class Directory(object):
""" This class stores values and notifies callbacks which were registered to be executed as soon
as some value is changed. This class works as DB cache mostly """
def __init__(self):
self.data = defaultdict(dict) # storage. A key is a slot name, a value is a dictionary with data
self.notify = defaultdict(lambda: defaultdict(list)) # registered callbacks: slot -> path -> handlers[]
@staticmethod
def get_slot_name(db, table):
""" Convert db, table pair into a slot name """
return db + "__" + table
def path_traverse(self, slot, path):
"""
Traverse a path in the storage.
If the path is an empty string, it returns a value as it is.
If the path is not an empty string, the method will traverse through the dictionary value.
Example:
self.data["key_1"] = { "abc": { "cde": { "fgh": "val_1", "ijk": "val_2" } } }
self.path_traverse("key_1", "abc/cde") will return True, { "fgh": "val_1", "ijk": "val_2" }
:param slot: storage key
:param path: storage path as a string where each internal key is separated by '/'
:return: a pair: True if the path was found, object if it was found
"""
if slot not in self.data:
return False, None
elif path == '':
return True, self.data[slot]
d = self.data[slot]
for p in path.split("/"):
if p not in d:
return False, None
d = d[p]
return True, d
def path_exist(self, db, table, path):
"""
Check if the path exists in the storage
:param db: db name
:param table: table name
:param path: requested path
:return: True if the path is available, False otherwise
"""
slot = self.get_slot_name(db, table)
return self.path_traverse(slot, path)[0]
def get_path(self, db, table, path):
"""
Return the requested path from the storage
:param db: db name
:param table: table name
:param path: requested path
:return: object if the path was found, None otherwise
"""
slot = self.get_slot_name(db, table)
return self.path_traverse(slot, path)[1]
def put(self, db, table, key, value):
"""
Put information into the storage. Notify handlers which are dependant to the information
:param db: db name
:param table: table name
:param key: key to change
:param value: value to put
:return:
"""
slot = self.get_slot_name(db, table)
self.data[slot][key] = value
if slot in self.notify:
for path in self.notify[slot].keys():
if self.path_exist(db, table, path):
for handler in self.notify[slot][path]:
handler()
def get(self, db, table, key):
"""
Get a value from the storage
:param db: db name
:param table: table name
:param key: ket to get
:return: value for the key
"""
slot = self.get_slot_name(db, table)
return self.data[slot][key]
def get_slot(self, db, table):
"""
Get an object from the storage
:param db: db name
:param table: table name
:return: object for the slot
"""
slot = self.get_slot_name(db, table)
return self.data[slot]
def remove(self, db, table, key):
"""
Remove a value from the storage
:param db: db name
:param table: table name
:param key: key to remove
"""
slot = self.get_slot_name(db, table)
if slot in self.data:
if key in self.data[slot]:
del self.data[slot][key]
else:
log_err("Directory: Can't remove key '%s' from slot '%s'. The key doesn't exist" % (key, slot))
else:
log_err("Directory: Can't remove key '%s' from slot '%s'. The slot doesn't exist" % (key, slot))
def remove_slot(self, db, table):
"""
Remove an object from the storage
:param db: db name
:param table: table name
"""
slot = self.get_slot_name(db, table)
if slot in self.data:
del self.data[slot]
else:
log_err("Directory: Can't remove slot '%s'. The slot doesn't exist" % slot)
def available(self, db, table):
"""
Check if the table is available
:param db: db name
:param table: table name
:return: True if the slot is available, False if not
"""
slot = self.get_slot_name(db, table)
return slot in self.data
def available_deps(self, deps):
"""
Check if all items from the deps list is available in the storage
:param deps: list of dependencies
:return: True if all dependencies are presented, False otherwise
"""
res = True
for db, table, path in deps:
res = res and self.path_exist(db, table, path)
return res
def subscribe(self, deps, handler):
"""
Subscribe the handler to be run as soon as all dependencies are presented
:param deps:
:param handler:
:return:
"""
for db, table, path in deps:
slot = self.get_slot_name(db, table)
self.notify[slot][path].append(handler)

View File

@ -1,71 +0,0 @@
from swsscommon import swsscommon
from app.log import log_debug, log_err
class Manager(object):
""" This class represents a SONiC DB table """
def __init__(self, common_objs, deps, database, table_name):
"""
Initialize class
:param common_objs: common object dictionary
:param deps: dependencies list
:param database: database name
:param table_name: table name
"""
self.directory = common_objs['directory']
self.cfg_mgr = common_objs['cfg_mgr']
self.constants = common_objs['constants']
self.deps = deps
self.db_name = database
self.table_name = table_name
self.set_queue = []
self.directory.subscribe(deps, self.on_deps_change) # subscribe this class method on directory changes
def get_database(self):
""" Return associated database """
return self.db_name
def get_table_name(self):
""" Return associated table name"""
return self.table_name
def handler(self, key, op, data):
"""
This method is executed on each add/remove event on the table.
:param key: key of the table entry
:param op: operation on the table entry. Could be either 'SET' or 'DEL'
:param data: associated data of the event. Empty for 'DEL' operation.
"""
if op == swsscommon.SET_COMMAND:
if self.directory.available_deps(self.deps): # all required dependencies are set in the Directory?
res = self.set_handler(key, data)
if not res: # set handler returned False, which means it is not ready to process is. Save it for later.
log_debug("'SET' handler returned NOT_READY for the Manager: %s" % self.__class__)
self.set_queue.append((key, data))
else:
log_debug("Not all dependencies are met for the Manager: %s" % self.__class__)
self.set_queue.append((key, data))
elif op == swsscommon.DEL_COMMAND:
self.del_handler(key)
else:
log_err("Invalid operation '%s' for key '%s'" % (op, key))
def on_deps_change(self):
""" This method is being executed on every dependency change """
if not self.directory.available_deps(self.deps):
return
new_queue = []
for key, data in self.set_queue:
res = self.set_handler(key, data)
if not res:
new_queue.append((key, data))
self.set_queue = new_queue
def set_handler(self, key, data):
""" Placeholder for 'SET' command """
log_err("set_handler() wasn't implemented for %s" % self.__class__.__name__)
def del_handler(self, key):
""" Placeholder for 'DEL' command """
log_err("del_handler wasn't implemented for %s" % self.__class__.__name__)

View File

@ -1 +1 @@
g_debug = True # FIXME: read from env variable, or from constants
g_debug = False

View File

@ -15,13 +15,10 @@ import jinja2
import netaddr
from swsscommon import swsscommon
from app.directory import Directory
from app.manager import Manager
from app.vars import g_debug
from app.log import log_debug, log_notice, log_info, log_warn, log_err, log_crit
from app.template import TemplateFabric
from app.config import ConfigMgr
from app.allow_list import BGPAllowListMgr
from app.util import run_command
g_run = True
@ -849,7 +846,7 @@ def wait_for_daemons(daemons, seconds):
def read_constants():
""" Read file with constants values from /etc/sonic/constants.yml """
with open('/etc/sonic/constants.yml') as fp:
content = yaml.load(fp) # FIXME: , Loader=yaml.FullLoader)
content = yaml.load(fp)
if "constants" not in content:
log_crit("/etc/sonic/constants.yml doesn't have 'constants' key")
raise Exception("/etc/sonic/constants.yml doesn't have 'constants' key")
@ -881,8 +878,6 @@ def main():
BGPPeerMgrBase(common_objs, "CONFIG_DB", swsscommon.CFG_BGP_NEIGHBOR_TABLE_NAME, "general", True),
BGPPeerMgrBase(common_objs, "CONFIG_DB", "BGP_MONITORS", "monitors", True),
BGPPeerMgrBase(common_objs, "CONFIG_DB", "BGP_PEER_RANGE", "dynamic", False),
# AllowList Managers
BGPAllowListMgr(common_objs, "CONFIG_DB", "BGP_ALLOWED_PREFIXES"),
]
runner = Runner()
for mgr in managers:

View File

@ -1,2 +0,0 @@
[pytest]
addopts = --cov=app --cov-report term

View File

@ -16,6 +16,5 @@ setuptools.setup(name='sonic-bgpcfgd',
]
},
install_requires=['jinja2>=2.10', 'netaddr', 'pyyaml'],
setup_requires=['pytest-runner'],
test_requires=['pytest', 'pytest-cov'],
setup_requires=['pytest-runner', 'pytest'],
)

View File

@ -4,14 +4,5 @@
"sub_role": "BackEnd"
}
},
"loopback0_ipv4": "10.10.10.10/32",
"constants": {
"bgp": {
"allow_list": {
"enabled": true,
"default_action": "permit",
"drop_community": "12345:12345"
}
}
}
"loopback0_ipv4": "10.10.10.10/32"
}

View File

@ -4,12 +4,5 @@
"sub_role": "NotBackEnd"
}
},
"loopback0_ipv4": "10.10.10.10/32",
"constants": {
"bgp": {
"allow_list": {
"enabled": false
}
}
}
"loopback0_ipv4": "10.10.10.10/32"
}

View File

@ -1,17 +0,0 @@
{
"CONFIG_DB__DEVICE_METADATA": {
"localhost": {
"sub_role": "BackEnd"
}
},
"loopback0_ipv4": "10.10.10.10/32",
"constants": {
"bgp": {
"allow_list": {
"enabled": true,
"default_action": "deny",
"drop_community": "12345:12345"
}
}
}
}

View File

@ -1,20 +1,6 @@
!
! template: bgpd/templates/general/policies.conf.j2
!
route-map ALLOW_LIST_DEPLOYMENT_ID_0_V4 permit 65535
set community 12345:12345 additive
!
route-map ALLOW_LIST_DEPLOYMENT_ID_0_V6 permit 65535
set community 12345:12345 additive
!
route-map FROM_BGP_PEER_V4 permit 2
call ALLOW_LIST_DEPLOYMENT_ID_0_V4
on-match next
!
route-map FROM_BGP_PEER_V6 permit 2
call ALLOW_LIST_DEPLOYMENT_ID_0_V6
on-match next
!
route-map FROM_BGP_PEER_V4 permit 100
!
route-map TO_BGP_PEER_V4 permit 100

View File

@ -1,39 +0,0 @@
!
! template: bgpd/templates/general/policies.conf.j2
!
route-map ALLOW_LIST_DEPLOYMENT_ID_0_V4 permit 65535
set community no-export additive
!
route-map ALLOW_LIST_DEPLOYMENT_ID_0_V6 permit 65535
set community no-export additive
!
route-map FROM_BGP_PEER_V4 permit 2
call ALLOW_LIST_DEPLOYMENT_ID_0_V4
on-match next
!
route-map FROM_BGP_PEER_V6 permit 2
call ALLOW_LIST_DEPLOYMENT_ID_0_V6
on-match next
!
route-map FROM_BGP_PEER_V4 permit 100
!
route-map TO_BGP_PEER_V4 permit 100
!
route-map FROM_BGP_PEER_V6 permit 1
set ipv6 next-hop prefer-global
!
route-map FROM_BGP_PEER_V6 permit 100
!
route-map TO_BGP_PEER_V6 permit 100
!
route-map FROM_BGP_PEER_V4_INT permit 2
set originator-id 10.10.10.10
!
route-map FROM_BGP_PEER_V6_INT permit 1
set ipv6 next-hop prefer-global
!
route-map FROM_BGP_PEER_V6_INT permit 2
set originator-id 10.10.10.10
!
! end of template: bgpd/templates/general/policies.conf.j2
!

View File

@ -1,469 +0,0 @@
from app.allow_list import BGPAllowListMgr
from app.directory import Directory
from app.template import TemplateFabric
import app
from mock import MagicMock
global_constants = {
"bgp": {
"allow_list": {
"enabled": True,
"default_pl_rules": {
"v4": [ "deny 0.0.0.0/0 le 17" ],
"v6": [
"deny 0::/0 le 59",
"deny 0::/0 ge 65"
]
}
}
}
}
def set_del_test(op, args, currect_config, expected_config):
set_del_test.push_list_called = False
def push_list(args):
set_del_test.push_list_called = True
assert args == expected_config
return True
#
app.allow_list.run_command = lambda cmd: (0, "", "")
#
cfg_mgr = MagicMock()
cfg_mgr.update.return_value = None
cfg_mgr.push_list = push_list
cfg_mgr.get_text.return_value = currect_config
common_objs = {
'directory': Directory(),
'cfg_mgr': cfg_mgr,
'tf': TemplateFabric(),
'constants': global_constants,
}
mgr = BGPAllowListMgr(common_objs, "CONFIG_DB", "BGP_ALLOWED_PREFIXES")
if op == "SET":
mgr.set_handler(*args)
elif op == "DEL":
mgr.del_handler(*args)
else:
assert False, "Wrong operation"
if expected_config:
assert set_del_test.push_list_called, "cfg_mgr.push_list wasn't called"
else:
assert not set_del_test.push_list_called, "cfg_mgr.push_list was called"
def test_set_handler_with_community():
set_del_test(
"SET",
("DEPLOYMENT_ID|5|1010:2020", {
"prefixes_v4": "10.20.30.0/24,30.50.0.0/16",
"prefixes_v6": "fc00:20::/64,fc00:30::/64",
}),
[],
[
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 10 deny 0.0.0.0/0 le 17',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 20 permit 10.20.30.0/24 ge 25',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 30 permit 30.50.0.0/16 ge 17',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 10 deny 0::/0 le 59',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 20 deny 0::/0 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 30 permit fc00:20::/64 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 40 permit fc00:30::/64 ge 65',
'bgp community-list standard COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020 permit 1010:2020',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V4 permit 10',
' match ip address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4',
' match community COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V6 permit 10',
' match ipv6 address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6',
' match community COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020',
]
)
def test_set_handler_no_community():
set_del_test(
"SET",
("DEPLOYMENT_ID|5", {
"prefixes_v4": "20.20.30.0/24,40.50.0.0/16",
"prefixes_v6": "fc01:20::/64,fc01:30::/64",
}),
[],
[
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 10 deny 0.0.0.0/0 le 17',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 20 permit 20.20.30.0/24 ge 25',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 30 permit 40.50.0.0/16 ge 17',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 10 deny 0::/0 le 59',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 20 deny 0::/0 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 30 permit fc01:20::/64 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 40 permit fc01:30::/64 ge 65',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V4 permit 30000',
' match ip address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V6 permit 30000',
' match ipv6 address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6',
]
)
def test_del_handler_with_community():
set_del_test(
"DEL",
("DEPLOYMENT_ID|5|1010:2020",),
[
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 10 deny 0.0.0.0/0 le 17',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 20 permit 10.20.30.0/24 ge 25',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 30 permit 30.50.0.0/16 ge 17',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 10 deny 0::/0 le 59',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 20 deny 0::/0 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 30 permit fc00:20::/64 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 40 permit fc00:30::/64 ge 65',
'bgp community-list standard COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020 permit 1010:2020',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V4 permit 10',
' match ip address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4',
' match community COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V6 permit 10',
' match ipv6 address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6',
' match community COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020',
""
],
[
'no route-map ALLOW_LIST_DEPLOYMENT_ID_5_V4 permit 10',
'no route-map ALLOW_LIST_DEPLOYMENT_ID_5_V6 permit 10',
'no ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4',
'no ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6',
'no bgp community-list standard COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020',
]
)
def test_del_handler_no_community():
set_del_test(
"DEL",
("DEPLOYMENT_ID|5",),
[
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 10 deny 0.0.0.0/0 le 17',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 20 permit 20.20.30.0/24 ge 25',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 30 permit 40.50.0.0/16 ge 17',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 10 deny 0::/0 le 59',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 20 deny 0::/0 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 30 permit fc01:20::/64 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 40 permit fc01:30::/64 ge 65',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V4 permit 30000',
' match ip address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V6 permit 30000',
' match ipv6 address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6',
" "
],
[
'no route-map ALLOW_LIST_DEPLOYMENT_ID_5_V4 permit 30000',
'no route-map ALLOW_LIST_DEPLOYMENT_ID_5_V6 permit 30000',
'no ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4',
'no ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6',
]
)
def test_set_handler_with_community_data_is_already_presented():
set_del_test(
"SET",
("DEPLOYMENT_ID|5|1010:2020", {
"prefixes_v4": "10.20.30.0/24,30.50.0.0/16",
"prefixes_v6": "fc00:20::/64,fc00:30::/64",
}),
[
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 10 deny 0.0.0.0/0 le 17',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 20 permit 10.20.30.0/24 ge 25',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 30 permit 30.50.0.0/16 ge 17',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 10 deny 0::/0 le 59',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 20 deny 0::/0 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 30 permit fc00:20::/64 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 40 permit fc00:30::/64 ge 65',
'bgp community-list standard COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020 permit 1010:2020',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V4 permit 10',
' match ip address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4',
' match community COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V6 permit 10',
' match ipv6 address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6',
' match community COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020',
""
],
[]
)
def test_set_handler_no_community_data_is_already_presented():
cfg_mgr = MagicMock()
cfg_mgr.update.return_value = None
cfg_mgr.get_text.return_value = [
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 10 deny 0.0.0.0/0 le 17',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 20 permit 20.20.30.0/24 ge 25',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 30 permit 40.50.0.0/16 ge 17',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 10 deny 0::/0 le 59',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 20 deny 0::/0 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 30 permit fc01:20::/64 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 40 permit fc01:30::/64 ge 65',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V4 permit 30000',
' match ip address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V6 permit 30000',
' match ipv6 address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6',
""
]
common_objs = {
'directory': Directory(),
'cfg_mgr': cfg_mgr,
'tf': TemplateFabric(),
'constants': global_constants,
}
mgr = BGPAllowListMgr(common_objs, "CONFIG_DB", "BGP_ALLOWED_PREFIXES")
mgr.set_handler("DEPLOYMENT_ID|5", {
"prefixes_v4": "20.20.30.0/24,40.50.0.0/16",
"prefixes_v6": "fc01:20::/64,fc01:30::/64",
})
assert not cfg_mgr.push_list.called, "cfg_mgr.push_list was called, but it shouldn't have been"
def test_del_handler_with_community_no_data():
set_del_test(
"DEL",
("DEPLOYMENT_ID|5|1010:2020",),
[""],
[]
)
def test_del_handler_no_community_no_data():
set_del_test(
"DEL",
("DEPLOYMENT_ID|5",),
[""],
[]
)
def test_set_handler_with_community_update_prefixes_add():
set_del_test(
"SET",
("DEPLOYMENT_ID|5|1010:2020", {
"prefixes_v4": "10.20.30.0/24,30.50.0.0/16,80.90.0.0/16",
"prefixes_v6": "fc00:20::/64,fc00:30::/64,fc02::/64",
}),
[
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 10 deny 0.0.0.0/0 le 17',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 20 permit 10.20.30.0/24 ge 25',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 30 permit 30.50.0.0/16 ge 17',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 10 deny 0::/0 le 59',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 20 deny 0::/0 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 30 permit fc00:20::/64 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 40 permit fc00:30::/64 ge 65',
'bgp community-list standard COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020 permit 1010:2020',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V4 permit 10',
' match ip address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4',
' match community COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V6 permit 10',
' match ipv6 address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6',
' match community COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020',
""
],
[
'no ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 10 deny 0.0.0.0/0 le 17',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 20 permit 10.20.30.0/24 ge 25',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 30 permit 30.50.0.0/16 ge 17',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V4 seq 40 permit 80.90.0.0/16 ge 17',
'no ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 10 deny 0::/0 le 59',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 20 deny 0::/0 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 30 permit fc00:20::/64 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 40 permit fc00:30::/64 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_1010:2020_V6 seq 50 permit fc02::/64 ge 65',
]
)
def test_set_handler_no_community_update_prefixes_add():
set_del_test(
"SET",
("DEPLOYMENT_ID|5", {
"prefixes_v4": "20.20.30.0/24,40.50.0.0/16,80.90.0.0/16",
"prefixes_v6": "fc01:20::/64,fc01:30::/64,fc02::/64",
}),
[
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 10 deny 0.0.0.0/0 le 17',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 20 permit 20.20.30.0/24 ge 25',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 30 permit 40.50.0.0/16 ge 17',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 10 deny 0::/0 le 59',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 20 deny 0::/0 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 30 permit fc01:20::/64 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 40 permit fc01:30::/64 ge 65',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V4 permit 30000',
' match ip address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4',
'route-map ALLOW_LIST_DEPLOYMENT_ID_5_V6 permit 30000',
' match ipv6 address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6',
""
],
[
'no ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 10 deny 0.0.0.0/0 le 17',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 20 permit 20.20.30.0/24 ge 25',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 30 permit 40.50.0.0/16 ge 17',
'ip prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V4 seq 40 permit 80.90.0.0/16 ge 17',
'no ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 10 deny 0::/0 le 59',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 20 deny 0::/0 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 30 permit fc01:20::/64 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 40 permit fc01:30::/64 ge 65',
'ipv6 prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_5_COMMUNITY_empty_V6 seq 50 permit fc02::/64 ge 65',
]
)
def test___set_handler_validate():
cfg_mgr = MagicMock()
common_objs = {
'directory': Directory(),
'cfg_mgr': cfg_mgr,
'tf': TemplateFabric(),
'constants': global_constants,
}
mgr = BGPAllowListMgr(common_objs, "CONFIG_DB", "BGP_ALLOWED_PREFIXES")
data = {
"prefixes_v4": "20.20.30.0/24,40.50.0.0/16",
"prefixes_v6": "fc01:20::/64,fc01:30::/64",
}
assert not mgr._BGPAllowListMgr__set_handler_validate("DEPLOYMENT_ID|5|1010:2020", None)
assert not mgr._BGPAllowListMgr__set_handler_validate("DEPLOYMENT_ID1|5|1010:2020", data)
assert not mgr._BGPAllowListMgr__set_handler_validate("DEPLOYMENT_ID|z|1010:2020", data)
assert not mgr._BGPAllowListMgr__set_handler_validate("DEPLOYMENT_ID|5|1010:2020", {
"prefixes_v4": "20.20.30.0/24,40.50.0.0/16",
"prefixes_v6": "20.20.30.0/24,40.50.0.0/16",
})
assert not mgr._BGPAllowListMgr__set_handler_validate("DEPLOYMENT_ID|5|1010:2020", {
"prefixes_v4": "fc01:20::/64,fc01:30::/64",
"prefixes_v6": "fc01:20::/64,fc01:30::/64",
})
def test___find_peer_group_by_deployment_id():
cfg_mgr = MagicMock()
cfg_mgr.update.return_value = None
cfg_mgr.get_text.return_value = [
'router bgp 64601',
' neighbor BGPSLBPassive peer-group',
' neighbor BGPSLBPassive remote-as 65432',
' neighbor BGPSLBPassive passive',
' neighbor BGPSLBPassive ebgp-multihop 255',
' neighbor BGPSLBPassive update-source 10.1.0.32',
' neighbor PEER_V4 peer-group',
' neighbor PEER_V4_INT peer-group',
' neighbor PEER_V6 peer-group',
' neighbor PEER_V6_INT peer-group',
' neighbor 10.0.0.1 remote-as 64802',
' neighbor 10.0.0.1 peer-group PEER_V4',
' neighbor 10.0.0.1 description ARISTA01T1',
' neighbor 10.0.0.1 timers 3 10',
' neighbor fc00::2 remote-as 64802',
' neighbor fc00::2 peer-group PEER_V6',
' neighbor fc00::2 description ARISTA01T1',
' neighbor fc00::2 timers 3 10',
' address-family ipv4 unicast',
' neighbor BGPSLBPassive activate',
' neighbor BGPSLBPassive soft-reconfiguration inbound',
' neighbor BGPSLBPassive route-map FROM_BGP_SPEAKER in',
' neighbor BGPSLBPassive route-map TO_BGP_SPEAKER out',
' neighbor PEER_V4 soft-reconfiguration inbound',
' neighbor PEER_V4 allowas-in 1',
' neighbor PEER_V4 route-map FROM_BGP_PEER_V4 in',
' neighbor PEER_V4 route-map TO_BGP_PEER_V4 out',
' neighbor PEER_V4_INT soft-reconfiguration inbound',
' neighbor PEER_V4_INT allowas-in 1',
' neighbor PEER_V4_INT route-map FROM_BGP_PEER_V4 in',
' neighbor PEER_V4_INT route-map TO_BGP_PEER_V4 out',
' neighbor 10.0.0.1 activate',
' exit-address-family',
' address-family ipv6 unicast',
' neighbor BGPSLBPassive activate',
' neighbor PEER_V6 soft-reconfiguration inbound',
' neighbor PEER_V6 allowas-in 1',
' neighbor PEER_V6 route-map FROM_BGP_PEER_V6 in',
' neighbor PEER_V6 route-map TO_BGP_PEER_V6 out',
' neighbor PEER_V6_INT soft-reconfiguration inbound',
' neighbor PEER_V6_INT allowas-in 1',
' neighbor PEER_V6_INT route-map FROM_BGP_PEER_V6 in',
' neighbor PEER_V6_INT route-map TO_BGP_PEER_V6 out',
' neighbor fc00::2 activate',
' exit-address-family',
'route-map ALLOW_LIST_DEPLOYMENT_ID_0_V4 permit 10',
' match community COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_0_COMMUNITY_1010:1010',
' match ip address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_0_COMMUNITY_1010:1010_V4',
'route-map ALLOW_LIST_DEPLOYMENT_ID_0_V4 permit 30000',
' match ip address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_0_COMMUNITY_empty_V4',
'route-map ALLOW_LIST_DEPLOYMENT_ID_0_V4 permit 65535',
' set community 5060:12345 additive',
'route-map ALLOW_LIST_DEPLOYMENT_ID_0_V6 permit 10',
' match community COMMUNITY_ALLOW_LIST_DEPLOYMENT_ID_0_COMMUNITY_1010:1010',
' match ipv6 address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_0_COMMUNITY_1010:1010_V6',
'route-map ALLOW_LIST_DEPLOYMENT_ID_0_V6 permit 30000',
' match ipv6 address prefix-list PL_ALLOW_LIST_DEPLOYMENT_ID_0_COMMUNITY_empty_V6',
'route-map ALLOW_LIST_DEPLOYMENT_ID_0_V6 permit 65535',
' set community 5060:12345 additive',
'route-map FROM_BGP_PEER_V4 permit 100',
'route-map FROM_BGP_PEER_V4 permit 2',
' call ALLOW_LIST_DEPLOYMENT_ID_0_V4',
' on-match next',
'route-map FROM_BGP_PEER_V6 permit 1',
' set ipv6 next-hop prefer-global ',
'route-map FROM_BGP_PEER_V6 permit 100',
'route-map FROM_BGP_PEER_V6 permit 2',
' call ALLOW_LIST_DEPLOYMENT_ID_0_V6',
' on-match next',
'route-map FROM_BGP_SPEAKER permit 10',
'route-map RM_SET_SRC permit 10',
' set src 10.1.0.32',
'route-map RM_SET_SRC6 permit 10',
' set src FC00:1::32',
'route-map TO_BGP_PEER_V4 permit 100',
'route-map TO_BGP_PEER_V6 permit 100',
'route-map TO_BGP_SPEAKER deny 1',
]
common_objs = {
'directory': Directory(),
'cfg_mgr': cfg_mgr,
'tf': TemplateFabric(),
'constants': global_constants,
}
mgr = BGPAllowListMgr(common_objs, "CONFIG_DB", "BGP_ALLOWED_PREFIXES")
values = mgr._BGPAllowListMgr__find_peer_group_by_deployment_id(0)
assert values == ['PEER_V4_INT', 'PEER_V6_INT', 'PEER_V6', 'PEER_V4']
def test___restart_peers_found_deployment_id():
test___restart_peers_found_deployment_id.run_command_counter = 0
def run_command(cmd):
output = [
['vtysh', '-c', 'clear bgp peer-group BGP_TEST_PEER_GROUP_1 soft in'],
['vtysh', '-c', 'clear bgp peer-group BGP_TEST_PEER_GROUP_2 soft in'],
]
desired_value = output[test___restart_peers_found_deployment_id.run_command_counter]
assert cmd == desired_value
test___restart_peers_found_deployment_id.run_command_counter += 1
return 0, "", ""
cfg_mgr = MagicMock()
common_objs = {
'directory': Directory(),
'cfg_mgr': cfg_mgr,
'tf': TemplateFabric(),
'constants': global_constants,
}
mgr = BGPAllowListMgr(common_objs, "CONFIG_DB", "BGP_ALLOWED_PREFIXES")
mocked = MagicMock(name='_BGPAllowListMgr__find_peer_group_by_deployment_id')
mocked.return_value = ["BGP_TEST_PEER_GROUP_1", "BGP_TEST_PEER_GROUP_2"]
mgr._BGPAllowListMgr__find_peer_group_by_deployment_id = mocked
app.allow_list.run_command = run_command
rc = mgr._BGPAllowListMgr__restart_peers(5)
assert rc
def test___restart_peers_not_found_deployment_id():
def run_command(cmd):
assert cmd == ['vtysh', '-c', 'clear bgp * soft in']
return 0, "", ""
cfg_mgr = MagicMock()
common_objs = {
'directory': Directory(),
'cfg_mgr': cfg_mgr,
'tf': TemplateFabric(),
'constants': global_constants,
}
mgr = BGPAllowListMgr(common_objs, "CONFIG_DB", "BGP_ALLOWED_PREFIXES")
mocked = MagicMock(name='_BGPAllowListMgr__find_peer_group_by_deployment_id')
mocked.return_value = []
mgr._BGPAllowListMgr__find_peer_group_by_deployment_id = mocked
app.allow_list.run_command = run_command
rc = mgr._BGPAllowListMgr__restart_peers(5)
assert rc
# FIXME: more testcases for coverage

View File

@ -77,30 +77,21 @@ def extract_rm_from_peer_group(path, peer_group_name):
return list(rm_set)
def check_routemap_in_file(filename, route_map_name):
route_map_re = re.compile(r'^route-map\s+%s\s+permit\s+(\d+)' % route_map_name)
route_map_re = re.compile(r'^route-map\s+%s\s+(\S+)' % route_map_name)
set_re = re.compile(r'set ipv6 next-hop prefer-global')
with open(filename) as fp:
lines = [line.strip() for line in fp if not line.strip().startswith('!') and line.strip() != '']
found_entry = False
found_seq_no = None
route_map_entries = {}
found_first_entry = False
for line in lines:
if found_entry:
route_map_entries[found_seq_no] = set_re.match(line) is not None
found_entry = False
found_seq_no = None
err_msg = "route-map %s doesn't have mandatory 'set ipv6 next-hop prefer-global' entry as the first rule" % route_map_name
assert not (found_first_entry and line.startswith("route-map")), err_msg
if found_first_entry and set_re.match(line):
break # We're good
if route_map_re.match(line):
found_seq_no = None
seq_n_txt = route_map_re.match(line).group(1)
assert seq_n_txt.isdigit(), "wrong sequence number for line '%s'" % line
found_seq_no = int(seq_n_txt)
assert found_seq_no not in route_map_entries, "Route-map has duplicate entries: %s - %d" % (route_map_name, found_seq_no)
found_entry = True
results = [route_map_entries[seq] for seq in sorted(route_map_entries.keys())]
if (len(results)):
err_msg = "route-map %s doesn't have mandatory permit entry for 'set ipv6 next-hop prefer-global" % route_map_name
assert results[0], err_msg
return len(results) > 0
err_msg = "route-map %s doesn't have mandatory permit entry for 'set ipv6 next-hop prefer-global" % route_map_name
assert route_map_re.match(line).group(1) == 'permit', err_msg
found_first_entry = True
return found_first_entry
def check_routemap(path, route_map_name):
result_files = load_results(path, "policies.conf")

View File

@ -5,7 +5,7 @@ CONSTANTS_PATH = os.path.abspath('../../files/image_config/constants/constants.y
def load_constants():
with open(CONSTANTS_PATH) as f:
data = yaml.load(f) # FIXME" , Loader=yaml.FullLoader)
data = yaml.load(f)
result = {}
assert "constants" in data, "'constants' key not found in constants.yml"
assert "bgp" in data["constants"], "'bgp' key not found in constants.yml"