diff --git a/dockers/docker-fpm-frr/bgpcfgd b/dockers/docker-fpm-frr/bgpcfgd new file mode 100755 index 000000000000..ac11bd129199 --- /dev/null +++ b/dockers/docker-fpm-frr/bgpcfgd @@ -0,0 +1,503 @@ +#!/usr/bin/env python + +import sys +import redis +import subprocess +import syslog +from swsssdk import ConfigDBConnector +import re +from netaddr.ip import IPAddress + +class BGPConfigDaemon: + + def _init_helper(self): + _localhost = self.config_db.get_entry('DEVICE_METADATA', 'localhost') + if _localhost and _localhost.has_key('bgp_asn') and _localhost['bgp_asn'].isdigit() and int(_localhost['bgp_asn']) > 0: + self.bgp_asn = _localhost['bgp_asn'] + else: + self.bgp_asn = -1 + self.bgp_neighbor = self.config_db.get_table('BGP_NEIGHBOR') + self.bgp_peer_range = self.config_db.get_table('BGP_PEER_RANGE') + self.vlan_interface = self.config_db.get_table('VLAN_INTERFACE') + self.loopback_interface = self.config_db.get_table('LOOPBACK_INTERFACE') + self.bgp_metadata = self.config_db.get_table('BGP_METADATA') + self.static_route = self.config_db.get_table('STATIC_ROUTE') + self.bgp_as_set = self.config_db.get_table('BGP_AS_SET') + self.bgp_community = self.config_db.get_table('BGP_COMMUNITY_SET') + self.bgp_policy = self.config_db.get_table('BGP_POLICY_ROUTE_MAP') + self.prefix_list = self.config_db.get_table('BGP_PREFIX_SET') + self.ip_access_list = self.config_db.get_table('IP_ACCESS_LIST') + + def __init__(self): + self.config_db = ConfigDBConnector() + self.config_db.connect() + self._init_helper() + + def __run_command(self, command): +# print command + syslog.syslog(syslog.LOG_INFO, 'cmd: {}'.format(command)) + p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) + stdout = p.communicate()[0] + p.wait() + if p.returncode != 0: + syslog.syslog(syslog.LOG_ERR, '[bgp cfgd] command execution returned {}. Command: "{}", stdout: "{}"'.format(p.returncode, command, stdout)) + + def _common_check(self): + if self.bgp_asn == -1: + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] No bgp_asn config.') + return False + else: + return True + + def metadata_handler(self, key, data, op_str): + if key == 'localhost' and data.has_key('bgp_asn'): + if data['bgp_asn'] != self.bgp_asn: + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] ASN changed to {} from {}, restart BGP...'.format(data['bgp_asn'], self.bgp_asn)) + self.__run_command("supervisorctl restart start.sh") + self.__run_command("service quagga restart") + self._init_helper() + + def _ipaddr_type(self, cfgstr): + ''' + return ip address version, or return False. + ''' + ip_type = False + # process prefix + if '/' in cfgstr: + text = cfgstr[:cfgstr.rfind('/')] + else: + text = cfgstr + # process addr + try: + addr = IPAddress(text) + ip_type = True + except: + ip_type = False + # return result + if ip_type: + return addr.version + else: + return False + + def _bgp_handler_add_common(self, key, data, address_type): + # add peer + if data.has_key('asn'): + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} remote-as {}'".format(self.bgp_asn, key, data['asn']) + self.__run_command(command) + # add name + if data.has_key('name'): + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} description {}'".format(self.bgp_asn, key, data['name']) + self.__run_command(command) + # add admin status + if data.has_key('admin_status'): + command_mod = 'no ' if data['admin_status'] == 'up' else '' + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c '{} neighbor {} shutdown'".format(self.bgp_asn, command_mod, key) + self.__run_command(command) + # add policy-in + if data.has_key('policy_in'): + policy_in_name_arr = data['policy_in'].split('|') + for name in policy_in_name_arr: + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} route-map {} in'".format(self.bgp_asn, key, name) + self.__run_command(command) + # add policy-out + if data.has_key('policy_out'): + policy_out_name_arr = data['policy_out'].split('|') + for name in policy_out_name_arr: + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} route-map {} out'".format(self.bgp_asn, key, name) + self.__run_command(command) + # add policy-import + if data.has_key('policy_import'): + policy_import_name_arr = data['policy_import'].split('|') + for name in policy_import_name_arr: + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} route-map {} import'".format(self.bgp_asn, key, name) + self.__run_command(command) + # add policy-export + if data.has_key('policy_export'): + policy_export_name_arr = data['policy_export'].split('|') + for name in policy_export_name_arr: + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} route-map {} export'".format(self.bgp_asn, key, name) + self.__run_command(command) + # add address-family + if address_type != "peer_group": + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'address-family {} unicast'".format(self.bgp_asn, address_type) + self.__run_command(command) + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} activate'".format(self.bgp_asn, key) + self.__run_command(command) + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'maximum-paths 64'".format(self.bgp_asn) + self.__run_command(command) + # no need to do 'exit-address-family' in quagga command line mode + + def bgp_handler(self, key, data, op_str): + ''' + handle bgp peer add/del. + ''' + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + if op_str == 'del': + # Neighbor is deleted + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'no neighbor {}'".format(self.bgp_asn, key) + self.__run_command(command) + if self.bgp_neighbor.has_key(key): + self.bgp_neighbor.pop(key) + else: + # Neighbor is added + address_type = "" + if self._ipaddr_type(key) == 4: + address_type = "ipv4" + elif self._ipaddr_type(key) == 6: + address_type = "ipv6" + else: + address_type = "peer_group" + # add peer-group + if address_type == "peer_group": + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} peer-group'".format(self.bgp_asn, key) + self.__run_command(command) + else: + if data.has_key('peer_group'): + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} peer-group {}'".format(self.bgp_asn, key, data['peer_group']) + self.__run_command(command) + # add common config + self._bgp_handler_add_common(key, data, address_type) + # store in local + self.bgp_neighbor[key] = data + + def bgp_peer_range_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + if op_str == 'del': + for ip in self.bgp_peer_range.get(key, {}).get("ip_range", []): + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'no bgp listen range {} peer-group {}'".format(self.bgp_asn, ip, key) + self.__run_command(command) + if self.bgp_peer_range.has_key(key): + self.bgp_peer_range.pop(key) + else: + for ip in self.bgp_peer_range.get(key, {}).get("ip_range", []): + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'bgp listen range {} peer-group {}'".format(self.bgp_asn, ip, key) + self.__run_command(command) + self.bgp_peer_range[key] = data + + def vlan_interface_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + ip = "" + tmp = key.split('|') + if len(tmp) is 2: + ip = tmp[1] + if op_str == 'del': + if ip != '': + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'no network {}'".format(self.bgp_asn, ip) + self.__run_command(command) + if self.vlan_interface.has_key(key): + self.vlan_interface.pop(key) + else: + if ip != '': + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'network {}'".format(self.bgp_asn, ip) + self.__run_command(command) + self.vlan_interface[key] = data + + def loopback_interface_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + ip = "" + tmp = key.split('|') + if len(tmp) is 2: + ip = tmp[1] + if op_str == 'del': + if ip != '': + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'no network {}'".format(self.bgp_asn, ip) + self.__run_command(command) + if self.loopback_interface.has_key(key): + self.loopback_interface.pop(key) + else: + if ip != '': + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'network {}'".format(self.bgp_asn, ip) + self.__run_command(command) + self.loopback_interface[key] = data + + def _bgp_redistribute_handle_update(self, request_str, local_str): + ''' + handle redistribute update + @request_str is array of request values, like 'connected|static' + @local_str is local stored array, same format of @request_str + ''' + r_arr = request_str.split('|') + l_arr = local_str.split('|') + # delete old value + for lv in l_arr: + if lv != '' and lv not in r_arr: + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'no redistribute {}'".format(self.bgp_asn, lv) + self.__run_command(command) + # add new value + for rv in r_arr: + if rv != '' and rv not in l_arr: + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'redistribute {}'".format(self.bgp_asn, rv) + self.__run_command(command) + + def bgp_metadata_handler(self, key, data, op_str): + ''' + handle bgp metadata, now only handle redistribute. + ''' + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + # handle redistribute + if key == 'localhost': + self._bgp_redistribute_handle_update(data.get('redistribute', ''), + self.bgp_metadata.get(key, {}).get('redistribute', '')) + self.bgp_metadata[key] = data + + def get_redistribute_attr(self): + bgp_metadata = self.config_db.get_table('BGP_METADATA') + res = bgp_metadata.get('localhost', {}).get('redistribute', '') + return res + + def _static_route_handler_helper(self, dip_np_str, metric_str): + arr = dip_np_str.split('|') + if len(arr) != 2: + return None, None, None + return arr[1], arr[1], metric_str.get('metric', '') + + def static_route_handler(self, key, data, op_str): + ''' + handle static route + ''' + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + if op_str == 'del': + if self.static_route.has_key(key): + dst_ip, nexthop_val, metric_val = self._static_route_handler_helper( + key, self.static_route[key]) + if dst_ip: + command = "vtysh -c 'configure terminal' -c 'no ip route {} {} {}'".format( + dst_ip, nexthop_val, metric_val) + self.__run_command(command) + self.static_route.pop(key) + else: + if not self.static_route.has_key(key): + dst_ip, nexthop_val, metric_val = self._static_route_handler_helper( + key, data) + if dst_ip: + command = "vtysh -c 'configure terminal' -c 'ip route {} {} {}'".format( + dst_ip, nexthop_val, metric_val) + self.__run_command(command) + self.static_route[key] = data + # set redistribute to trigger + res = self.get_redistribute_attr() + res = res.split('|') + for rv in res: + if rv == 'static': + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'redistribute {}'".format( + self.bgp_asn, rv) + self.__run_command(command) + + def bgp_as_path_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + if op_str == 'del': + if self.bgp_as_set.has_key(key): + command = "vtysh -c 'configure terminal' -c 'no ip as-path access-list {}'".format(key) + self.__run_command(command) + self.bgp_as_set.pop(key) + else: + as_path_action = data.get('action', '') + as_path_line = data.get('line', '') + command = "vtysh -c 'configure terminal' -c 'ip as-path access-list {} {} {}'".format(key, as_path_action, as_path_line) + self.__run_command(command) + self.bgp_as_set[key] = data + + def bgp_community_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + command = '' + if op_str == 'del': + if self.bgp_community.has_key(key): + data = self.bgp_community[key] + bgp_community_action = data.get('action', '') + bgp_community_line = data.get('line', '') + bgp_community_type = data.get('name_type', 'standard') + if key.isdigit(): + command = "vtysh -c 'configure terminal' -c 'no ip community-list {} {} {}'".format(key, bgp_community_action, bgp_community_line) + else: + command = "vtysh -c 'configure terminal' -c 'no ip community-list {} {} {} {}'".format(bgp_community_type, key, bgp_community_action, bgp_community_line) + self.__run_command(command) + self.bgp_community.pop(key) + else: + bgp_community_action = data.get('action', '') + bgp_community_line = data.get('line', '') + bgp_community_type = data.get('name_type', 'standard') + if key.isdigit(): + command = "vtysh -c 'configure terminal' -c 'ip community-list {} {} {}'".format(key, bgp_community_action, bgp_community_line) + else: + command = "vtysh -c 'configure terminal' -c 'ip community-list {} {} {} {}'".format(bgp_community_type, key, bgp_community_action, bgp_community_line) + self.__run_command(command) + self.bgp_community[key] = data + + def bgp_policy_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + if op_str == 'del': + if self.bgp_policy.has_key(key): + route_map_key_arr = key.split('|') + if len(route_map_key_arr) == 3: + command = "vtysh -c 'configure terminal' -c 'no route-map {}'".format( + route_map_key_arr[0], route_map_key_arr[1], route_map_key_arr[2]) + self.__run_command(command) + self.bgp_policy.pop(key) + else: + # get name, action, term + route_map_key_arr = key.split('|') + if len(route_map_key_arr) != 3: + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] got error in key {}'.format(key)) + return + route_map_name = route_map_key_arr[0] + route_map_action = route_map_key_arr[1] + route_map_term = route_map_key_arr[2] + # get others + route_map_description = data.get('description', '') + route_map_match_as_path = data.get('match_as_path', '') + route_map_set_as_path = data.get('set_as_path', '') + route_map_match_prefix_list = data.get('match_prefix_list', '') + route_map_set_metric = data.get('set_metric', '') + route_map_set_community = data.get('set_community', '') + route_map_set_local_preference = data.get('set_local_preference', '') + route_map_set_ip_next_hop = data.get('set_ip_next_hop', '') + # add route-map + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}'".format(route_map_name, route_map_action, route_map_term) + self.__run_command(command) + if route_map_description != '': + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'description {}'".format( + route_map_name, route_map_action, route_map_term, route_map_description) + self.__run_command(command) + if route_map_match_as_path != '': + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'match as-path {}'".format( + route_map_name, route_map_action, route_map_term, route_map_match_as_path) + self.__run_command(command) + if route_map_set_as_path != '': + if route_map_set_as_path.isdigit(): + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'set as-path prepend last-as {}'".format( + route_map_name, route_map_action, route_map_term, route_map_set_as_path) + else: + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'set as-path prepend {}'".format( + route_map_name, route_map_action, route_map_term, route_map_set_as_path) + self.__run_command(command) + if route_map_match_prefix_list != '': + name_arr = route_map_match_prefix_list.split('|') + ip_version = 'ip' + prefix_list_name = '' + if len(name_arr) >= 2: + ip_version = name_arr[0] + prefix_list_name = name_arr[1] + else: + prefix_list_name = name_arr[0] + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'match {} address prefix-list {}'".format( + route_map_name, route_map_action, route_map_term, ip_version, prefix_list_name) + self.__run_command(command) + if route_map_set_metric != '': + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'set metric {}'".format( + route_map_name, route_map_action, route_map_term, route_map_set_metric) + self.__run_command(command) + if route_map_set_community != '': + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'set community {}'".format( + route_map_name, route_map_action, route_map_term, route_map_set_community) + self.__run_command(command) + if route_map_set_local_preference != '': + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'set local-preference {}'".format( + route_map_name, route_map_action, route_map_term, route_map_set_local_preference) + self.__run_command(command) + if route_map_set_ip_next_hop != '': + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'set ip next-hop {}'".format( + route_map_name, route_map_action, route_map_term, route_map_set_ip_next_hop) + self.__run_command(command) + self.bgp_policy[key] = data + + def bgp_prefix_list_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + arr = key.split('|') + if op_str == 'del': + if self.prefix_list.has_key(key): + if len(arr) == 3: + name = arr[1] + command = "vtysh -c 'configure terminal' -c 'no ip prefix-list {}'".format(name) + self.__run_command(command) + self.prefix_list.pop(key) + else: + if len(arr) == 3: + ip_addr = arr[0] + name = arr[1] + action = arr[2] + compare = data.get('compare', '') + length = data.get('length', '') + if compare != '' and length != '': + compare = compare + ' ' + length + else: + compare = '' + seq = data.get('seq', '') + if seq != '': + seq = 'seq ' + seq + command = "vtysh -c 'configure terminal' -c 'ip prefix-list {} {} {} {} {}'".format(name, seq, action, ip_addr, compare) + self.__run_command(command) + self.prefix_list[key] = data + + def ip_access_list_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + if op_str == 'del': + if self.ip_access_list.has_key(key): + acc_arr = key.split('|') + if len(acc_arr) == 2: + command = "vtysh -c 'configure terminal' -c 'no access-list {} {}'".format(acc_arr[0], acc_arr[1]) + self.__run_command(command) + self.ip_access_list.pop(key) + else: + if not self.ip_access_list.has_key(key): + acc_arr = key.split('|') + if len(acc_arr) == 2: + command = "vtysh -c 'configure terminal' -c 'access-list {} {}'".format(acc_arr[0], acc_arr[1]) + self.__run_command(command) + self.ip_access_list[key] = data + + def start(self): + self.config_db.subscribe('BGP_NEIGHBOR', + lambda table, key, data, op_str: self.bgp_handler(key, data, op_str)) + self.config_db.subscribe('DEVICE_METADATA', + lambda table, key, data, op_str: self.metadata_handler(key, data, op_str)) + self.config_db.subscribe('BGP_PEER_RANGE', + lambda table, key, data, op_str: self.bgp_peer_range_handler(key, data, op_str)) + self.config_db.subscribe('VLAN_INTERFACE', + lambda table, key, data, op_str: self.vlan_interface_handler(key, data, op_str)) + self.config_db.subscribe('LOOPBACK_INTERFACE', + lambda table, key, data, op_str: self.loopback_interface_handler(key, data, op_str)) + self.config_db.subscribe('BGP_METADATA', + lambda table, key, data, op_str: self.bgp_metadata_handler(key, data, op_str)) + self.config_db.subscribe('STATIC_ROUTE', + lambda table, key, data, op_str: self.static_route_handler(key, data, op_str)) + self.config_db.subscribe('BGP_AS_SET', + lambda table, key, data, op_str: self.bgp_as_path_handler(key, data, op_str)) + self.config_db.subscribe('BGP_COMMUNITY_SET', + lambda table, key, data, op_str: self.bgp_community_handler(key, data, op_str)) + self.config_db.subscribe('BGP_POLICY_ROUTE_MAP', + lambda table, key, data, op_str: self.bgp_policy_handler(key, data, op_str)) + self.config_db.subscribe('BGP_PREFIX_SET', + lambda table, key, data, op_str: self.bgp_prefix_list_handler(key, data, op_str)) + self.config_db.subscribe('IP_ACCESS_LIST', + lambda table, key, data, op_str: self.ip_access_list_handler(key, data, op_str)) + self.config_db.listen_with_op() + + +def main(): + daemon = BGPConfigDaemon() + daemon.start() + +if __name__ == "__main__": + main() diff --git a/dockers/docker-fpm-frr/frr.conf.j2 b/dockers/docker-fpm-frr/frr.conf.j2 index d4fdec897da2..6874c0ddd715 100644 --- a/dockers/docker-fpm-frr/frr.conf.j2 +++ b/dockers/docker-fpm-frr/frr.conf.j2 @@ -1,3 +1,70 @@ +{# bgp_neighbor_detail #} +{% macro bgp_neighbor_detail(neighbor_addr, bgp_session) -%} +{% if bgp_session.has_key('asn') and bgp_session['asn'] | int != 0 %} + neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }} + neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }} +{% if bgp_session.has_key('policy_in') %} +{% set policy_in_arr = bgp_session['policy_in'].split('|') %} +{% for policy_in in policy_in_arr %} + neighbor {{ neighbor_addr }} route-map {{ policy_in }} in +{% endfor %} +{% endif %} +{% if bgp_session.has_key('policy_out') %} +{% set policy_out_arr = bgp_session['policy_out'].split('|') %} +{% for policy_out in policy_out_arr %} + neighbor {{ neighbor_addr }} route-map {{ policy_out }} out +{% endfor %} +{% endif %} +{% if bgp_session.has_key('policy_import') %} +{% set policy_import_arr = bgp_session['policy_import'].split('|') %} +{% for policy_import in policy_import_arr %} + neighbor {{ neighbor_addr }} route-map {{ policy_import }} import +{% endfor %} +{% endif %} +{% if bgp_session.has_key('policy_export') %} +{% set policy_export_arr = bgp_session['policy_export'].split('|') %} +{% for policy_export in policy_export_arr %} + neighbor {{ neighbor_addr }} route-map {{ policy_export }} export +{% endfor %} +{% endif %} +{% if bgp_session.has_key('password') %} + neighbor {{ neighbor_addr }} password {{ bgp_session['password'] }} +{% endif %} +{% if bgp_session.has_key('ebgp_multihop') %} + neighbor {{ neighbor_addr }} ebgp-multihop {{ bgp_session['ebgp_multihop'] }} +{% endif %} +{% if bgp_session.has_key('prefix_in') %} +{% set prefix_in_arr = bgp_session['prefix_in'].split('|') %} +{% for prefix_in in prefix_in_arr %} + neighbor {{ neighbor_addr }} prefix-list {{ prefix_in }} in +{% endfor %} +{% endif %} +{% if bgp_session.has_key('prefix_out') %} +{% set prefix_out_arr = bgp_session['prefix_out'].split('|') %} +{% for prefix_out in prefix_out_arr %} + neighbor {{ neighbor_addr }} prefix-list {{ prefix_out }} out +{% endfor %} +{% endif %} +{% if bgp_session.has_key('admin_status') and bgp_session['admin_status'] == 'down' or not bgp_session.has_key('admin_status') and DEVICE_METADATA['localhost'].has_key('default_bgp_status') and DEVICE_METADATA['localhost']['default_bgp_status'] == 'down' %} + neighbor {{ neighbor_addr }} shutdown +{% endif %} +{% set default_maximum_paths = 32 %} +{% if bgp_session.has_key('maximum_paths') %} +{% set default_maximum_paths = bgp_session['maximum_paths'] %} +{% endif %} +{% if neighbor_addr | ipv4 %} + address-family ipv4 + neighbor {{ neighbor_addr }} activate + maximum-paths {{ default_maximum_paths }} + exit-address-family +{% elif neighbor_addr | ipv6 %} + address-family ipv6 + neighbor {{ neighbor_addr }} activate + maximum-paths {{ default_maximum_paths }} + exit-address-family +{% endif %} +{% endif %} +{%- endmacro %} ! {% block banner %} ! =========== Managed by sonic-cfggen DO NOT edit manually! ==================== @@ -28,15 +95,6 @@ link-detect {% endfor %} {% endblock interfaces %} ! -{% block default_route %} -! set static default route to mgmt gateway as a backup to learned default -{% for (name, prefix) in MGMT_INTERFACE %} -{% if prefix | ipv4 %} -ip route 0.0.0.0/0 {{ MGMT_INTERFACE[(name, prefix)]['gwaddr'] }} 200 -{% endif %} -{% endfor %} -{% endblock default_route %} -! {% block source_loopback %} {% set lo_ipv4_addrs = [] %} {% set lo_ipv6_addrs = [] %} @@ -57,7 +115,7 @@ ip route 0.0.0.0/0 {{ MGMT_INTERFACE[(name, prefix)]['gwaddr'] }} 200 route-map RM_SET_SRC permit 10 set src {{ lo_ipv4_addrs[0] | ip }} ! -{% if lo_ipv6_addrs|length > 0 %} +{% if lo_ipv6_addrs|length > 0 %} route-map RM_SET_SRC6 permit 10 set src {{ lo_ipv6_addrs[0] | ip }} ! @@ -70,7 +128,19 @@ ipv6 protocol bgp route-map RM_SET_SRC6 {% endif %} {% endblock source_loopback %} ! -{% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %} +{% block static_route %} +! set static route by user +{% for sr in STATIC_ROUTE %} +{% set dstip_val = sr[0] %} +{% set nexthop_val = sr[1] %} +{% if dstip_val | ipv4 or dstip_val | ipv6 %} +ip route {{ dstip_val }} {{ nexthop_val }} {{ STATIC_ROUTE[sr]['ad']|default('') }} +{% endif %} +{% endfor %} +{% endblock static_route %} +! +{# BGP local and session configures #} +{% if DEVICE_METADATA['localhost'].has_key('bgp_asn') and DEVICE_METADATA['localhost']['bgp_asn'] | int %} {% block bgp_init %} ! ! bgp multiple-instance @@ -83,27 +153,44 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} bgp log-neighbor-changes bgp bestpath as-path multipath-relax no bgp default ipv4-unicast -{# Advertise graceful restart capability for ToR #} -{% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} +{% if BGP_METADATA and BGP_METADATA.has_key('localhost') %} +{# distribute route #} +{% if BGP_METADATA['localhost']['redistribute'] and BGP_METADATA['localhost']['redistribute'] != '' %} +{% set redistribute_arr = BGP_METADATA['localhost']['redistribute'].split('|') %} +{% for arr in redistribute_arr %} + redistribute {{ arr }} +{% endfor %} +{% endif %} +{# Advertise graceful restart capability #} +{% if BGP_METADATA['localhost'].has_key('graceful_restart_param') %} bgp graceful-restart +{% set gr_params = BGP_METADATA['localhost']['graceful_restart_param'].split('|') %} +{% for gr_param in gr_params %} + bgp graceful-restart {{ gr_param }} +{% endfor %} +{% endif %} {% endif %} +{# Router ID #} +{% if BGP_METADATA and BGP_METADATA.has_key('localhost') and BGP_METADATA['localhost'].has_key('router_id') %} + bgp router-id {{ BGP_METADATA['localhost']['router_id'] }} +{% else %} {% for (name, prefix) in LOOPBACK_INTERFACE %} {% if prefix | ipv4 and name == 'Loopback0' %} bgp router-id {{ prefix | ip }} {% endif %} {% endfor %} +{% endif %} {# advertise loopback #} {% for (name, prefix) in LOOPBACK_INTERFACE %} {% if prefix | ipv4 and name == 'Loopback0' %} network {{ prefix | ip }}/32 {% elif prefix | ipv6 and name == 'Loopback0' %} address-family ipv6 - network {{ prefix | ip }}/128 + network {{ prefix | ip }}/64 exit-address-family {% endif %} {% endfor %} {% endblock bgp_init %} -{% endif %} {% block vlan_advertisement %} {% for (name, prefix) in VLAN_INTERFACE %} {% if prefix | ipv4 %} @@ -116,55 +203,20 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% endfor %} {% endblock vlan_advertisement %} {% block bgp_sessions %} +{% if BGP_NEIGHBOR %} {% for neighbor_addr, bgp_session in BGP_NEIGHBOR.iteritems() %} -{% if bgp_session['asn'] | int != 0 %} - neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }} - neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }} -{# set the bgp neighbor timers if they have not default values #} -{% if (bgp_session['keepalive'] is defined and bgp_session['keepalive'] | int != 60) - or (bgp_session['holdtime'] is defined and bgp_session['holdtime'] | int != 180) %} - neighbor {{ neighbor_addr }} timers {{ bgp_session['keepalive'] }} {{ bgp_session['holdtime'] }} -{% endif %} -{% if bgp_session.has_key('admin_status') and bgp_session['admin_status'] == 'down' or not bgp_session.has_key('admin_status') and DEVICE_METADATA['localhost'].has_key('default_bgp_status') and DEVICE_METADATA['localhost']['default_bgp_status'] == 'down' %} - neighbor {{ neighbor_addr }} shutdown -{% endif %} -{% if neighbor_addr | ipv4 %} - address-family ipv4 -{% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} - neighbor {{ neighbor_addr }} allowas-in 1 -{% endif %} - neighbor {{ neighbor_addr }} activate - neighbor {{ neighbor_addr }} soft-reconfiguration inbound -{% if bgp_session['rrclient'] | int != 0 %} - neighbor {{ neighbor_addr }} route-reflector-client -{% endif %} -{% if bgp_session['nhopself'] | int != 0 %} - neighbor {{ neighbor_addr }} next-hop-self -{% endif %} - maximum-paths 64 - exit-address-family -{% endif %} -{% if neighbor_addr | ipv6 %} - address-family ipv6 -{% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} - neighbor {{ neighbor_addr }} allowas-in 1 -{% endif %} - neighbor {{ neighbor_addr }} activate - neighbor {{ neighbor_addr }} soft-reconfiguration inbound -{% if bgp_session['rrclient'] | int != 0 %} - neighbor {{ neighbor_addr }} route-reflector-client -{% endif %} -{% if bgp_session['nhopself'] | int != 0 %} - neighbor {{ neighbor_addr }} next-hop-self -{% endif %} -{% if bgp_session['asn'] != DEVICE_METADATA['localhost']['bgp_asn'] %} - neighbor {{ neighbor_addr }} route-map set-next-hop-global-v6 in -{% endif %} - maximum-paths 64 - exit-address-family +{# add peer-group block: if neighbor_addr is not IPADDR, then it's peer_group #} +{% if neighbor_addr | ipv4 or neighbor_addr | ipv6 %} +{% if bgp_session.has_key('peer_group') %} + neighbor {{ neighbor_addr }} peer-group {{ bgp_session['peer_group'] }} {% endif %} +{{ bgp_neighbor_detail(neighbor_addr, bgp_session) }} +{% else %} + neighbor {{ neighbor_addr }} peer-group +{{ bgp_neighbor_detail(neighbor_addr, bgp_session) }} {% endif %} {% endfor %} +{% endif %} {% endblock bgp_sessions %} {% block bgp_peers_with_range %} {% if BGP_PEER_RANGE %} @@ -173,37 +225,104 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} neighbor {{ bgp_peer['name'] }} passive neighbor {{ bgp_peer['name'] }} remote-as {{ deployment_id_asn_map[DEVICE_METADATA['localhost']['deployment_id']] }} neighbor {{ bgp_peer['name'] }} ebgp-multihop 255 + neighbor {{ bgp_peer['name'] }} soft-reconfiguration inbound {% for (name, prefix) in LOOPBACK_INTERFACE %} {% if name == 'Loopback1' %} neighbor {{ bgp_peer['name'] }} update-source {{ prefix | ip }} {% endif %} {% endfor %} + neighbor {{ bgp_peer['name'] }} route-map FROM_BGP_SPEAKER_V4 in + neighbor {{ bgp_peer['name'] }} route-map TO_BGP_SPEAKER_V4 out {% for ip_range in bgp_peer['ip_range'] %} bgp listen range {{ip_range}} peer-group {{ bgp_peer['name'] }} {% endfor %} address-family ipv4 neighbor {{ bgp_peer['name'] }} activate - neighbor {{ bgp_peer['name'] }} soft-reconfiguration inbound - neighbor {{ bgp_peer['name'] }} route-map FROM_BGP_SPEAKER_V4 in - neighbor {{ bgp_peer['name'] }} route-map TO_BGP_SPEAKER_V4 out maximum-paths 64 exit-address-family address-family ipv6 neighbor {{ bgp_peer['name'] }} activate - neighbor {{ bgp_peer['name'] }} soft-reconfiguration inbound maximum-paths 64 exit-address-family {% endfor %} {% endif %} {% endblock bgp_peers_with_range %} +{% endif %} ! +{# BGP policies #} {% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %} -maximum-paths 64 +{% set default_maximum_paths = 32 %} +{% if BGP_METADATA and BGP_METADATA.has_key('localhost') and BGP_METADATA['localhost'].has_key('maximum_paths') %} +{% set default_maximum_paths = BGP_METADATA['localhost']['maximum_paths'] %} +{% endif %} +maximum-paths {{ default_maximum_paths }} ! route-map ISOLATE permit 10 set as-path prepend {{ DEVICE_METADATA['localhost']['bgp_asn'] }} +! +{# access-list block #} +{% if IP_ACCESS_LIST %} +{% for (name, rules) in IP_ACCESS_LIST.iteritems() %} +{% for rule in rules %} +access-list {{ name }} {{ rule }} +{% endfor %} +{% endfor %} +{% endif %} +! +{# ip prefix block #} +{% if BGP_PREFIX_SET %} +{% for name, name_data in BGP_PREFIX_SET.iteritems() %} +{% if name_data.has_key('seq') %} +{% set seq_str = 'seq ' + name_data['seq'] %} +{% endif %} +ip prefix-list {{ name[1] }} {{ seq_str }} {{ name[2] }} prefix {{ name[0] }} {{ name_data['compare'] }} {{ name_data['length'] }} +{% endfor %} {% endif %} ! -route-map set-next-hop-global-v6 permit 10 -set ipv6 next-hop prefer-global +{# ip community block #} +{% if BGP_COMMUNITY_SET %} +{% for name, name_data in BGP_COMMUNITY_SET.iteritems() %} +ip community-list {{ name }} {{ name_data['action'] }} {{ name_data['line'] }} +{% endfor %} +{% endif %} +! +{# ip as_path block #} +{% if BGP_AS_SET %} +{% for name, name_data in BGP_AS_SET.iteritems() %} +ip as-path access-list {{ name }} {{ name_data['action'] }} {{ name_data['line'] }} +{% endfor %} +{% endif %} +! +{# route-map block #} +{% if BGP_POLICY_ROUTE_MAP %} +{% for policy_name, policy_body in BGP_POLICY_ROUTE_MAP.iteritems() %} +route-map {{ policy_name[0] }} {{ policy_name[1] }} {{ policy_name[2] }} +{% for match_name, action_term in policy_body.iteritems() %} +{% if match_name == "description" %} + description {{ action_term }} +{% elif match_name == "match_as_path" %} + match as-path {{ action_term }} +{% elif match_name == "set_as_path" %} +{% if action_term | int %} + set as-path prepend last-as {{ action_term }} +{% else %} + set as-path prepend {{ action_term }} +{% endif %} +{% elif match_name == "match_prefix_list" %} +{% set action_term_arr = action_term.split('|') %} + match {{ action_term_arr[0] }} address prefix-list {{ action_term_arr[1] }} +{% elif match_name == "set_metric" %} + set metric {{ action_term }} +{% elif match_name == "set_community" %} + set community {{ action_term }} +{% elif match_name == "set_local_preference" %} + set local-preference {{ action_term }} +{% elif match_name == "set_ip_next_hop" %} + set ip next-hop {{ action_term }} +{% endif %} +{% endfor %} +{% endfor %} +{% endif %} +{# end of route-map block #} ! +{% endif %} \ No newline at end of file diff --git a/dockers/docker-fpm-frr/start.sh b/dockers/docker-fpm-frr/start.sh index f46a42b4c34c..aa944bd826c1 100755 --- a/dockers/docker-fpm-frr/start.sh +++ b/dockers/docker-fpm-frr/start.sh @@ -4,3 +4,4 @@ rm -f /var/run/rsyslogd.pid service rsyslog start service frr start fpmsyncd & +bgpcfgd & \ No newline at end of file diff --git a/dockers/docker-fpm-quagga/bgp_retain_restart.sh b/dockers/docker-fpm-quagga/bgp_retain_restart.sh new file mode 100755 index 000000000000..6c34a639bb16 --- /dev/null +++ b/dockers/docker-fpm-quagga/bgp_retain_restart.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +run_cmd () { + echo "$1" + eval $1 +} + +echo "Restart bgpd" + +run_cmd "sonic-cfggen -d -y /etc/sonic/deployment_id_asn_map.yml -t /usr/share/sonic/templates/bgpd.conf.j2 > /etc/quagga/bgpd.conf" +run_cmd "supervisorctl stop bgpd" +run_cmd "sleep 1" +run_cmd "supervisorctl start bgpd" \ No newline at end of file diff --git a/dockers/docker-fpm-quagga/bgpcfgd b/dockers/docker-fpm-quagga/bgpcfgd index 012a766c20b9..ac11bd129199 100755 --- a/dockers/docker-fpm-quagga/bgpcfgd +++ b/dockers/docker-fpm-quagga/bgpcfgd @@ -5,56 +5,494 @@ import redis import subprocess import syslog from swsssdk import ConfigDBConnector +import re +from netaddr.ip import IPAddress class BGPConfigDaemon: + def _init_helper(self): + _localhost = self.config_db.get_entry('DEVICE_METADATA', 'localhost') + if _localhost and _localhost.has_key('bgp_asn') and _localhost['bgp_asn'].isdigit() and int(_localhost['bgp_asn']) > 0: + self.bgp_asn = _localhost['bgp_asn'] + else: + self.bgp_asn = -1 + self.bgp_neighbor = self.config_db.get_table('BGP_NEIGHBOR') + self.bgp_peer_range = self.config_db.get_table('BGP_PEER_RANGE') + self.vlan_interface = self.config_db.get_table('VLAN_INTERFACE') + self.loopback_interface = self.config_db.get_table('LOOPBACK_INTERFACE') + self.bgp_metadata = self.config_db.get_table('BGP_METADATA') + self.static_route = self.config_db.get_table('STATIC_ROUTE') + self.bgp_as_set = self.config_db.get_table('BGP_AS_SET') + self.bgp_community = self.config_db.get_table('BGP_COMMUNITY_SET') + self.bgp_policy = self.config_db.get_table('BGP_POLICY_ROUTE_MAP') + self.prefix_list = self.config_db.get_table('BGP_PREFIX_SET') + self.ip_access_list = self.config_db.get_table('IP_ACCESS_LIST') + def __init__(self): self.config_db = ConfigDBConnector() self.config_db.connect() - self.bgp_asn = self.config_db.get_entry('DEVICE_METADATA', 'localhost')['bgp_asn'] - self.bgp_neighbor = self.config_db.get_table('BGP_NEIGHBOR') + self._init_helper() def __run_command(self, command): # print command + syslog.syslog(syslog.LOG_INFO, 'cmd: {}'.format(command)) p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) stdout = p.communicate()[0] p.wait() if p.returncode != 0: syslog.syslog(syslog.LOG_ERR, '[bgp cfgd] command execution returned {}. Command: "{}", stdout: "{}"'.format(p.returncode, command, stdout)) - def metadata_handler(self, key, data): + def _common_check(self): + if self.bgp_asn == -1: + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] No bgp_asn config.') + return False + else: + return True + + def metadata_handler(self, key, data, op_str): if key == 'localhost' and data.has_key('bgp_asn'): if data['bgp_asn'] != self.bgp_asn: syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] ASN changed to {} from {}, restart BGP...'.format(data['bgp_asn'], self.bgp_asn)) self.__run_command("supervisorctl restart start.sh") self.__run_command("service quagga restart") - self.bgp_asn = data['bgp_asn'] + self._init_helper() + + def _ipaddr_type(self, cfgstr): + ''' + return ip address version, or return False. + ''' + ip_type = False + # process prefix + if '/' in cfgstr: + text = cfgstr[:cfgstr.rfind('/')] + else: + text = cfgstr + # process addr + try: + addr = IPAddress(text) + ip_type = True + except: + ip_type = False + # return result + if ip_type: + return addr.version + else: + return False + + def _bgp_handler_add_common(self, key, data, address_type): + # add peer + if data.has_key('asn'): + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} remote-as {}'".format(self.bgp_asn, key, data['asn']) + self.__run_command(command) + # add name + if data.has_key('name'): + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} description {}'".format(self.bgp_asn, key, data['name']) + self.__run_command(command) + # add admin status + if data.has_key('admin_status'): + command_mod = 'no ' if data['admin_status'] == 'up' else '' + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c '{} neighbor {} shutdown'".format(self.bgp_asn, command_mod, key) + self.__run_command(command) + # add policy-in + if data.has_key('policy_in'): + policy_in_name_arr = data['policy_in'].split('|') + for name in policy_in_name_arr: + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} route-map {} in'".format(self.bgp_asn, key, name) + self.__run_command(command) + # add policy-out + if data.has_key('policy_out'): + policy_out_name_arr = data['policy_out'].split('|') + for name in policy_out_name_arr: + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} route-map {} out'".format(self.bgp_asn, key, name) + self.__run_command(command) + # add policy-import + if data.has_key('policy_import'): + policy_import_name_arr = data['policy_import'].split('|') + for name in policy_import_name_arr: + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} route-map {} import'".format(self.bgp_asn, key, name) + self.__run_command(command) + # add policy-export + if data.has_key('policy_export'): + policy_export_name_arr = data['policy_export'].split('|') + for name in policy_export_name_arr: + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} route-map {} export'".format(self.bgp_asn, key, name) + self.__run_command(command) + # add address-family + if address_type != "peer_group": + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'address-family {} unicast'".format(self.bgp_asn, address_type) + self.__run_command(command) + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} activate'".format(self.bgp_asn, key) + self.__run_command(command) + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'maximum-paths 64'".format(self.bgp_asn) + self.__run_command(command) + # no need to do 'exit-address-family' in quagga command line mode - def bgp_handler(self, key, data): + def bgp_handler(self, key, data, op_str): + ''' + handle bgp peer add/del. + ''' + if self._common_check() == False: + return syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) - if not data: + if op_str == 'del': # Neighbor is deleted command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'no neighbor {}'".format(self.bgp_asn, key) self.__run_command(command) - self.bgp_neighbor.pop(key) + if self.bgp_neighbor.has_key(key): + self.bgp_neighbor.pop(key) else: - command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} remote-as {}'".format(self.bgp_asn, key, data['asn']) + # Neighbor is added + address_type = "" + if self._ipaddr_type(key) == 4: + address_type = "ipv4" + elif self._ipaddr_type(key) == 6: + address_type = "ipv6" + else: + address_type = "peer_group" + # add peer-group + if address_type == "peer_group": + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} peer-group'".format(self.bgp_asn, key) + self.__run_command(command) + else: + if data.has_key('peer_group'): + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} peer-group {}'".format(self.bgp_asn, key, data['peer_group']) + self.__run_command(command) + # add common config + self._bgp_handler_add_common(key, data, address_type) + # store in local + self.bgp_neighbor[key] = data + + def bgp_peer_range_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + if op_str == 'del': + for ip in self.bgp_peer_range.get(key, {}).get("ip_range", []): + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'no bgp listen range {} peer-group {}'".format(self.bgp_asn, ip, key) + self.__run_command(command) + if self.bgp_peer_range.has_key(key): + self.bgp_peer_range.pop(key) + else: + for ip in self.bgp_peer_range.get(key, {}).get("ip_range", []): + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'bgp listen range {} peer-group {}'".format(self.bgp_asn, ip, key) + self.__run_command(command) + self.bgp_peer_range[key] = data + + def vlan_interface_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + ip = "" + tmp = key.split('|') + if len(tmp) is 2: + ip = tmp[1] + if op_str == 'del': + if ip != '': + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'no network {}'".format(self.bgp_asn, ip) + self.__run_command(command) + if self.vlan_interface.has_key(key): + self.vlan_interface.pop(key) + else: + if ip != '': + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'network {}'".format(self.bgp_asn, ip) + self.__run_command(command) + self.vlan_interface[key] = data + + def loopback_interface_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + ip = "" + tmp = key.split('|') + if len(tmp) is 2: + ip = tmp[1] + if op_str == 'del': + if ip != '': + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'no network {}'".format(self.bgp_asn, ip) + self.__run_command(command) + if self.loopback_interface.has_key(key): + self.loopback_interface.pop(key) + else: + if ip != '': + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'network {}'".format(self.bgp_asn, ip) + self.__run_command(command) + self.loopback_interface[key] = data + + def _bgp_redistribute_handle_update(self, request_str, local_str): + ''' + handle redistribute update + @request_str is array of request values, like 'connected|static' + @local_str is local stored array, same format of @request_str + ''' + r_arr = request_str.split('|') + l_arr = local_str.split('|') + # delete old value + for lv in l_arr: + if lv != '' and lv not in r_arr: + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'no redistribute {}'".format(self.bgp_asn, lv) + self.__run_command(command) + # add new value + for rv in r_arr: + if rv != '' and rv not in l_arr: + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'redistribute {}'".format(self.bgp_asn, rv) + self.__run_command(command) + + def bgp_metadata_handler(self, key, data, op_str): + ''' + handle bgp metadata, now only handle redistribute. + ''' + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + # handle redistribute + if key == 'localhost': + self._bgp_redistribute_handle_update(data.get('redistribute', ''), + self.bgp_metadata.get(key, {}).get('redistribute', '')) + self.bgp_metadata[key] = data + + def get_redistribute_attr(self): + bgp_metadata = self.config_db.get_table('BGP_METADATA') + res = bgp_metadata.get('localhost', {}).get('redistribute', '') + return res + + def _static_route_handler_helper(self, dip_np_str, metric_str): + arr = dip_np_str.split('|') + if len(arr) != 2: + return None, None, None + return arr[1], arr[1], metric_str.get('metric', '') + + def static_route_handler(self, key, data, op_str): + ''' + handle static route + ''' + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + if op_str == 'del': + if self.static_route.has_key(key): + dst_ip, nexthop_val, metric_val = self._static_route_handler_helper( + key, self.static_route[key]) + if dst_ip: + command = "vtysh -c 'configure terminal' -c 'no ip route {} {} {}'".format( + dst_ip, nexthop_val, metric_val) + self.__run_command(command) + self.static_route.pop(key) + else: + if not self.static_route.has_key(key): + dst_ip, nexthop_val, metric_val = self._static_route_handler_helper( + key, data) + if dst_ip: + command = "vtysh -c 'configure terminal' -c 'ip route {} {} {}'".format( + dst_ip, nexthop_val, metric_val) + self.__run_command(command) + self.static_route[key] = data + # set redistribute to trigger + res = self.get_redistribute_attr() + res = res.split('|') + for rv in res: + if rv == 'static': + command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'redistribute {}'".format( + self.bgp_asn, rv) + self.__run_command(command) + + def bgp_as_path_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + if op_str == 'del': + if self.bgp_as_set.has_key(key): + command = "vtysh -c 'configure terminal' -c 'no ip as-path access-list {}'".format(key) + self.__run_command(command) + self.bgp_as_set.pop(key) + else: + as_path_action = data.get('action', '') + as_path_line = data.get('line', '') + command = "vtysh -c 'configure terminal' -c 'ip as-path access-list {} {} {}'".format(key, as_path_action, as_path_line) + self.__run_command(command) + self.bgp_as_set[key] = data + + def bgp_community_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + command = '' + if op_str == 'del': + if self.bgp_community.has_key(key): + data = self.bgp_community[key] + bgp_community_action = data.get('action', '') + bgp_community_line = data.get('line', '') + bgp_community_type = data.get('name_type', 'standard') + if key.isdigit(): + command = "vtysh -c 'configure terminal' -c 'no ip community-list {} {} {}'".format(key, bgp_community_action, bgp_community_line) + else: + command = "vtysh -c 'configure terminal' -c 'no ip community-list {} {} {} {}'".format(bgp_community_type, key, bgp_community_action, bgp_community_line) + self.__run_command(command) + self.bgp_community.pop(key) + else: + bgp_community_action = data.get('action', '') + bgp_community_line = data.get('line', '') + bgp_community_type = data.get('name_type', 'standard') + if key.isdigit(): + command = "vtysh -c 'configure terminal' -c 'ip community-list {} {} {}'".format(key, bgp_community_action, bgp_community_line) + else: + command = "vtysh -c 'configure terminal' -c 'ip community-list {} {} {} {}'".format(bgp_community_type, key, bgp_community_action, bgp_community_line) + self.__run_command(command) + self.bgp_community[key] = data + + def bgp_policy_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + if op_str == 'del': + if self.bgp_policy.has_key(key): + route_map_key_arr = key.split('|') + if len(route_map_key_arr) == 3: + command = "vtysh -c 'configure terminal' -c 'no route-map {}'".format( + route_map_key_arr[0], route_map_key_arr[1], route_map_key_arr[2]) + self.__run_command(command) + self.bgp_policy.pop(key) + else: + # get name, action, term + route_map_key_arr = key.split('|') + if len(route_map_key_arr) != 3: + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] got error in key {}'.format(key)) + return + route_map_name = route_map_key_arr[0] + route_map_action = route_map_key_arr[1] + route_map_term = route_map_key_arr[2] + # get others + route_map_description = data.get('description', '') + route_map_match_as_path = data.get('match_as_path', '') + route_map_set_as_path = data.get('set_as_path', '') + route_map_match_prefix_list = data.get('match_prefix_list', '') + route_map_set_metric = data.get('set_metric', '') + route_map_set_community = data.get('set_community', '') + route_map_set_local_preference = data.get('set_local_preference', '') + route_map_set_ip_next_hop = data.get('set_ip_next_hop', '') + # add route-map + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}'".format(route_map_name, route_map_action, route_map_term) self.__run_command(command) - if data.has_key('name'): - command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} description {}'".format(self.bgp_asn, key, data['name']) + if route_map_description != '': + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'description {}'".format( + route_map_name, route_map_action, route_map_term, route_map_description) self.__run_command(command) - if data.has_key('admin_status'): - command_mod = 'no ' if data['admin_status'] == 'up' else '' - command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c '{}neighbor {} shutdown'".format(self.bgp_asn, command_mod, key) + if route_map_match_as_path != '': + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'match as-path {}'".format( + route_map_name, route_map_action, route_map_term, route_map_match_as_path) self.__run_command(command) - self.bgp_neighbor[key] = data + if route_map_set_as_path != '': + if route_map_set_as_path.isdigit(): + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'set as-path prepend last-as {}'".format( + route_map_name, route_map_action, route_map_term, route_map_set_as_path) + else: + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'set as-path prepend {}'".format( + route_map_name, route_map_action, route_map_term, route_map_set_as_path) + self.__run_command(command) + if route_map_match_prefix_list != '': + name_arr = route_map_match_prefix_list.split('|') + ip_version = 'ip' + prefix_list_name = '' + if len(name_arr) >= 2: + ip_version = name_arr[0] + prefix_list_name = name_arr[1] + else: + prefix_list_name = name_arr[0] + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'match {} address prefix-list {}'".format( + route_map_name, route_map_action, route_map_term, ip_version, prefix_list_name) + self.__run_command(command) + if route_map_set_metric != '': + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'set metric {}'".format( + route_map_name, route_map_action, route_map_term, route_map_set_metric) + self.__run_command(command) + if route_map_set_community != '': + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'set community {}'".format( + route_map_name, route_map_action, route_map_term, route_map_set_community) + self.__run_command(command) + if route_map_set_local_preference != '': + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'set local-preference {}'".format( + route_map_name, route_map_action, route_map_term, route_map_set_local_preference) + self.__run_command(command) + if route_map_set_ip_next_hop != '': + command = "vtysh -c 'configure terminal' -c 'route-map {} {} {}' -c 'set ip next-hop {}'".format( + route_map_name, route_map_action, route_map_term, route_map_set_ip_next_hop) + self.__run_command(command) + self.bgp_policy[key] = data + + def bgp_prefix_list_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + arr = key.split('|') + if op_str == 'del': + if self.prefix_list.has_key(key): + if len(arr) == 3: + name = arr[1] + command = "vtysh -c 'configure terminal' -c 'no ip prefix-list {}'".format(name) + self.__run_command(command) + self.prefix_list.pop(key) + else: + if len(arr) == 3: + ip_addr = arr[0] + name = arr[1] + action = arr[2] + compare = data.get('compare', '') + length = data.get('length', '') + if compare != '' and length != '': + compare = compare + ' ' + length + else: + compare = '' + seq = data.get('seq', '') + if seq != '': + seq = 'seq ' + seq + command = "vtysh -c 'configure terminal' -c 'ip prefix-list {} {} {} {} {}'".format(name, seq, action, ip_addr, compare) + self.__run_command(command) + self.prefix_list[key] = data + + def ip_access_list_handler(self, key, data, op_str): + if self._common_check() == False: + return + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + if op_str == 'del': + if self.ip_access_list.has_key(key): + acc_arr = key.split('|') + if len(acc_arr) == 2: + command = "vtysh -c 'configure terminal' -c 'no access-list {} {}'".format(acc_arr[0], acc_arr[1]) + self.__run_command(command) + self.ip_access_list.pop(key) + else: + if not self.ip_access_list.has_key(key): + acc_arr = key.split('|') + if len(acc_arr) == 2: + command = "vtysh -c 'configure terminal' -c 'access-list {} {}'".format(acc_arr[0], acc_arr[1]) + self.__run_command(command) + self.ip_access_list[key] = data def start(self): - self.config_db.subscribe('BGP_NEIGHBOR', - lambda table, key, data: self.bgp_handler(key, data)) + self.config_db.subscribe('BGP_NEIGHBOR', + lambda table, key, data, op_str: self.bgp_handler(key, data, op_str)) self.config_db.subscribe('DEVICE_METADATA', - lambda table, key, data: self.metadata_handler(key, data)) - self.config_db.listen() + lambda table, key, data, op_str: self.metadata_handler(key, data, op_str)) + self.config_db.subscribe('BGP_PEER_RANGE', + lambda table, key, data, op_str: self.bgp_peer_range_handler(key, data, op_str)) + self.config_db.subscribe('VLAN_INTERFACE', + lambda table, key, data, op_str: self.vlan_interface_handler(key, data, op_str)) + self.config_db.subscribe('LOOPBACK_INTERFACE', + lambda table, key, data, op_str: self.loopback_interface_handler(key, data, op_str)) + self.config_db.subscribe('BGP_METADATA', + lambda table, key, data, op_str: self.bgp_metadata_handler(key, data, op_str)) + self.config_db.subscribe('STATIC_ROUTE', + lambda table, key, data, op_str: self.static_route_handler(key, data, op_str)) + self.config_db.subscribe('BGP_AS_SET', + lambda table, key, data, op_str: self.bgp_as_path_handler(key, data, op_str)) + self.config_db.subscribe('BGP_COMMUNITY_SET', + lambda table, key, data, op_str: self.bgp_community_handler(key, data, op_str)) + self.config_db.subscribe('BGP_POLICY_ROUTE_MAP', + lambda table, key, data, op_str: self.bgp_policy_handler(key, data, op_str)) + self.config_db.subscribe('BGP_PREFIX_SET', + lambda table, key, data, op_str: self.bgp_prefix_list_handler(key, data, op_str)) + self.config_db.subscribe('IP_ACCESS_LIST', + lambda table, key, data, op_str: self.ip_access_list_handler(key, data, op_str)) + self.config_db.listen_with_op() def main(): diff --git a/dockers/docker-fpm-quagga/bgpd.conf.j2 b/dockers/docker-fpm-quagga/bgpd.conf.j2 index f00cea64577d..c5957ac65ef4 100644 --- a/dockers/docker-fpm-quagga/bgpd.conf.j2 +++ b/dockers/docker-fpm-quagga/bgpd.conf.j2 @@ -1,4 +1,70 @@ -! +{# bgp_neighbor_detail #} +{% macro bgp_neighbor_detail(neighbor_addr, bgp_session) -%} +{% if bgp_session.has_key('asn') and bgp_session['asn'] | int != 0 %} + neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }} + neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }} +{% if bgp_session.has_key('policy_in') %} +{% set policy_in_arr = bgp_session['policy_in'].split('|') %} +{% for policy_in in policy_in_arr %} + neighbor {{ neighbor_addr }} route-map {{ policy_in }} in +{% endfor %} +{% endif %} +{% if bgp_session.has_key('policy_out') %} +{% set policy_out_arr = bgp_session['policy_out'].split('|') %} +{% for policy_out in policy_out_arr %} + neighbor {{ neighbor_addr }} route-map {{ policy_out }} out +{% endfor %} +{% endif %} +{% if bgp_session.has_key('policy_import') %} +{% set policy_import_arr = bgp_session['policy_import'].split('|') %} +{% for policy_import in policy_import_arr %} + neighbor {{ neighbor_addr }} route-map {{ policy_import }} import +{% endfor %} +{% endif %} +{% if bgp_session.has_key('policy_export') %} +{% set policy_export_arr = bgp_session['policy_export'].split('|') %} +{% for policy_export in policy_export_arr %} + neighbor {{ neighbor_addr }} route-map {{ policy_export }} export +{% endfor %} +{% endif %} +{% if bgp_session.has_key('password') %} + neighbor {{ neighbor_addr }} password {{ bgp_session['password'] }} +{% endif %} +{% if bgp_session.has_key('ebgp_multihop') %} + neighbor {{ neighbor_addr }} ebgp-multihop {{ bgp_session['ebgp_multihop'] }} +{% endif %} +{% if bgp_session.has_key('prefix_in') %} +{% set prefix_in_arr = bgp_session['prefix_in'].split('|') %} +{% for prefix_in in prefix_in_arr %} + neighbor {{ neighbor_addr }} prefix-list {{ prefix_in }} in +{% endfor %} +{% endif %} +{% if bgp_session.has_key('prefix_out') %} +{% set prefix_out_arr = bgp_session['prefix_out'].split('|') %} +{% for prefix_out in prefix_out_arr %} + neighbor {{ neighbor_addr }} prefix-list {{ prefix_out }} out +{% endfor %} +{% endif %} +{% if bgp_session.has_key('admin_status') and bgp_session['admin_status'] == 'down' or not bgp_session.has_key('admin_status') and DEVICE_METADATA['localhost'].has_key('default_bgp_status') and DEVICE_METADATA['localhost']['default_bgp_status'] == 'down' %} + neighbor {{ neighbor_addr }} shutdown +{% endif %} +{% set default_maximum_paths = 32 %} +{% if bgp_session.has_key('maximum_paths') %} +{% set default_maximum_paths = bgp_session['maximum_paths'] %} +{% endif %} +{% if neighbor_addr | ipv4 %} + address-family ipv4 + neighbor {{ neighbor_addr }} activate + maximum-paths {{ default_maximum_paths }} + exit-address-family +{% elif neighbor_addr | ipv6 %} + address-family ipv6 + neighbor {{ neighbor_addr }} activate + maximum-paths {{ default_maximum_paths }} + exit-address-family +{% endif %} +{% endif %} +{%- endmacro %} {% block banner %} ! =========== Managed by sonic-cfggen DO NOT edit manually! ==================== ! generated by templates/quagga/bgpd.conf.j2 with config DB data @@ -14,7 +80,8 @@ log facility local4 ! enable password {# {{ en_passwd }} TODO: param needed #} {% endblock system_init %} ! -{% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %} +{# BGP local and session configures #} +{% if DEVICE_METADATA['localhost'].has_key('bgp_asn') and DEVICE_METADATA['localhost']['bgp_asn'] | int %} {% block bgp_init %} ! ! bgp multiple-instance @@ -27,13 +94,33 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} bgp log-neighbor-changes bgp bestpath as-path multipath-relax no bgp default ipv4-unicast - bgp graceful-restart restart-time 180 +{% if BGP_METADATA and BGP_METADATA.has_key('localhost') %} +{# distribute route #} +{% if BGP_METADATA['localhost']['redistribute'] and BGP_METADATA['localhost']['redistribute'] != '' %} +{% set redistribute_arr = BGP_METADATA['localhost']['redistribute'].split('|') %} +{% for arr in redistribute_arr %} + redistribute {{ arr }} +{% endfor %} +{% endif %} +{# Advertise graceful restart capability #} +{% if BGP_METADATA['localhost'].has_key('graceful_restart_param') %} bgp graceful-restart +{% set gr_params = BGP_METADATA['localhost']['graceful_restart_param'].split('|') %} +{% for gr_param in gr_params %} + bgp graceful-restart {{ gr_param }} +{% endfor %} +{% endif %} +{% endif %} +{# Router ID #} +{% if BGP_METADATA and BGP_METADATA.has_key('localhost') and BGP_METADATA['localhost'].has_key('router_id') %} + bgp router-id {{ BGP_METADATA['localhost']['router_id'] }} +{% else %} {% for (name, prefix) in LOOPBACK_INTERFACE %} {% if prefix | ipv4 and name == 'Loopback0' %} bgp router-id {{ prefix | ip }} {% endif %} {% endfor %} +{% endif %} {# advertise loopback #} {% for (name, prefix) in LOOPBACK_INTERFACE %} {% if prefix | ipv4 and name == 'Loopback0' %} @@ -45,7 +132,6 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% endif %} {% endfor %} {% endblock bgp_init %} -{% endif %} {% block vlan_advertisement %} {% for (name, prefix) in VLAN_INTERFACE %} {% if prefix | ipv4 %} @@ -58,40 +144,20 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% endfor %} {% endblock vlan_advertisement %} {% block bgp_sessions %} +{% if BGP_NEIGHBOR %} {% for neighbor_addr, bgp_session in BGP_NEIGHBOR.iteritems() %} -{% if bgp_session['asn'] | int != 0 %} - neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }} - neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }} -{# set the bgp neighbor timers if they have not default values #} -{% if (bgp_session['keepalive'] is defined and bgp_session['keepalive'] | int != 60) - or (bgp_session['holdtime'] is defined and bgp_session['holdtime'] | int != 180) %} - neighbor {{ neighbor_addr }} timers {{ bgp_session['keepalive'] }} {{ bgp_session['holdtime'] }} -{% endif %} -{% if bgp_session.has_key('admin_status') and bgp_session['admin_status'] == 'down' or not bgp_session.has_key('admin_status') and DEVICE_METADATA['localhost'].has_key('default_bgp_status') and DEVICE_METADATA['localhost']['default_bgp_status'] == 'down' %} - neighbor {{ neighbor_addr }} shutdown -{% endif %} -{% if neighbor_addr | ipv4 %} - address-family ipv4 -{% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} - neighbor {{ neighbor_addr }} allowas-in 1 -{% endif %} - neighbor {{ neighbor_addr }} activate - neighbor {{ neighbor_addr }} soft-reconfiguration inbound - maximum-paths 64 - exit-address-family -{% endif %} -{% if neighbor_addr | ipv6 %} - address-family ipv6 -{% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} - neighbor {{ neighbor_addr }} allowas-in 1 -{% endif %} - neighbor {{ neighbor_addr }} activate - neighbor {{ neighbor_addr }} soft-reconfiguration inbound - maximum-paths 64 - exit-address-family +{# add peer-group block: if neighbor_addr is not IPADDR, then it's peer_group #} +{% if neighbor_addr | ipv4 or neighbor_addr | ipv6 %} +{% if bgp_session.has_key('peer_group') %} + neighbor {{ neighbor_addr }} peer-group {{ bgp_session['peer_group'] }} {% endif %} +{{ bgp_neighbor_detail(neighbor_addr, bgp_session) }} +{% else %} + neighbor {{ neighbor_addr }} peer-group +{{ bgp_neighbor_detail(neighbor_addr, bgp_session) }} {% endif %} {% endfor %} +{% endif %} {% endblock bgp_sessions %} {% block bgp_peers_with_range %} {% if BGP_PEER_RANGE %} @@ -122,11 +188,82 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% endfor %} {% endif %} {% endblock bgp_peers_with_range %} +{% endif %} ! +{# BGP policies #} {% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %} -maximum-paths 64 +{% set default_maximum_paths = 32 %} +{% if BGP_METADATA and BGP_METADATA.has_key('localhost') and BGP_METADATA['localhost'].has_key('maximum_paths') %} +{% set default_maximum_paths = BGP_METADATA['localhost']['maximum_paths'] %} +{% endif %} +maximum-paths {{ default_maximum_paths }} ! route-map ISOLATE permit 10 set as-path prepend {{ DEVICE_METADATA['localhost']['bgp_asn'] }} +! +{# access-list block #} +{% if IP_ACCESS_LIST %} +{% for (name, rules) in IP_ACCESS_LIST.iteritems() %} +{% for rule in rules %} +access-list {{ name }} {{ rule }} +{% endfor %} +{% endfor %} +{% endif %} +! +{# ip prefix block #} +{% if BGP_PREFIX_SET %} +{% for name, name_data in BGP_PREFIX_SET.iteritems() %} +{% if name_data.has_key('seq') %} +{% set seq_str = 'seq ' + name_data['seq'] %} +{% endif %} +ip prefix-list {{ name[1] }} {{ seq_str }} {{ name[2] }} prefix {{ name[0] }} {{ name_data['compare'] }} {{ name_data['length'] }} +{% endfor %} {% endif %} ! +{# ip community block #} +{% if BGP_COMMUNITY_SET %} +{% for name, name_data in BGP_COMMUNITY_SET.iteritems() %} +ip community-list {{ name }} {{ name_data['action'] }} {{ name_data['line'] }} +{% endfor %} +{% endif %} +! +{# ip as_path block #} +{% if BGP_AS_SET %} +{% for name, name_data in BGP_AS_SET.iteritems() %} +ip as-path access-list {{ name }} {{ name_data['action'] }} {{ name_data['line'] }} +{% endfor %} +{% endif %} +! +{# route-map block #} +{% if BGP_POLICY_ROUTE_MAP %} +{% for policy_name, policy_body in BGP_POLICY_ROUTE_MAP.iteritems() %} +route-map {{ policy_name[0] }} {{ policy_name[1] }} {{ policy_name[2] }} +{% for match_name, action_term in policy_body.iteritems() %} +{% if match_name == "description" %} + description {{ action_term }} +{% elif match_name == "match_as_path" %} + match as-path {{ action_term }} +{% elif match_name == "set_as_path" %} +{% if action_term | int %} + set as-path prepend last-as {{ action_term }} +{% else %} + set as-path prepend {{ action_term }} +{% endif %} +{% elif match_name == "match_prefix_list" %} +{% set action_term_arr = action_term.split('|') %} + match {{ action_term_arr[0] }} address prefix-list {{ action_term_arr[1] }} +{% elif match_name == "set_metric" %} + set metric {{ action_term }} +{% elif match_name == "set_community" %} + set community {{ action_term }} +{% elif match_name == "set_local_preference" %} + set local-preference {{ action_term }} +{% elif match_name == "set_ip_next_hop" %} + set ip next-hop {{ action_term }} +{% endif %} +{% endfor %} +{% endfor %} +{% endif %} +{# end of route-map block #} +! +{% endif %} diff --git a/dockers/docker-fpm-quagga/supervisord.conf b/dockers/docker-fpm-quagga/supervisord.conf index c4351fafefe9..3a0f54c464b4 100644 --- a/dockers/docker-fpm-quagga/supervisord.conf +++ b/dockers/docker-fpm-quagga/supervisord.conf @@ -31,7 +31,7 @@ stdout_logfile=syslog stderr_logfile=syslog [program:zebra] -command=/usr/lib/quagga/zebra -A 127.0.0.1 +command=/usr/lib/quagga/zebra -A 127.0.0.1 -r priority=4 autostart=false autorestart=false @@ -40,7 +40,7 @@ stdout_logfile=syslog stderr_logfile=syslog [program:bgpd] -command=/usr/lib/quagga/bgpd -A 127.0.0.1 -F +command=/usr/lib/quagga/bgpd -A 127.0.0.1 -F -r priority=5 stopsignal=KILL autostart=false diff --git a/dockers/docker-fpm-quagga/zebra.conf.j2 b/dockers/docker-fpm-quagga/zebra.conf.j2 index 4acb474b0e35..1bbc41a42da8 100644 --- a/dockers/docker-fpm-quagga/zebra.conf.j2 +++ b/dockers/docker-fpm-quagga/zebra.conf.j2 @@ -35,6 +35,17 @@ ip route 0.0.0.0/0 {{ MGMT_INTERFACE[(name, prefix)]['gwaddr'] }} 200 {% endfor %} {% endblock default_route %} ! +{% block static_route %} +! set static route by user +{% for sr in STATIC_ROUTE %} +{% set dstip_val = sr[0] %} +{% set nexthop_val = sr[1] %} +{% if dstip_val | ipv4 or dstip_val | ipv6 %} +ip route {{ dstip_val }} {{ nexthop_val }} {{ STATIC_ROUTE[sr]['ad']|default('') }} +{% endif %} +{% endfor %} +{% endblock static_route %} +! {% block source_loopback %} {% set lo_ipv4_addrs = [] %} {% set lo_ipv6_addrs = [] %} @@ -52,19 +63,17 @@ ip route 0.0.0.0/0 {{ MGMT_INTERFACE[(name, prefix)]['gwaddr'] }} 200 {% endfor %} {% endif %} ! Set ip source to loopback for bgp learned routes -{% if lo_ipv4_addrs|length > 0 -%} +{% if lo_ipv4_addrs|length > 0 %} route-map RM_SET_SRC permit 10 set src {{ lo_ipv4_addrs[0] | ip }} ! +ip protocol bgp route-map RM_SET_SRC +! {% endif %} -{% if lo_ipv6_addrs|length > 0 %} +{% if lo_ipv6_addrs|length > 0 %} route-map RM_SET_SRC6 permit 10 set src {{ lo_ipv6_addrs[0] | ip }} ! -{% endif %} -ip protocol bgp route-map RM_SET_SRC -! -{% if lo_ipv6_addrs|length > 0 %} ipv6 protocol bgp route-map RM_SET_SRC6 ! {% endif %}