[ConfigDB] Move all BGP configuration into DB (#861)

- BGP data read from minigraph.py now match DB schema
- BGP templates are updated
- bgpcfgd can now deal with runtime neighbor create/delete
This commit is contained in:
Taoyu Li 2017-08-08 16:23:58 -07:00 committed by GitHub
parent afbf1ee2fb
commit a2fe0212be
18 changed files with 159 additions and 169 deletions

View File

@ -18,7 +18,7 @@ log facility local4
! !
! bgp multiple-instance ! bgp multiple-instance
! !
router bgp {{ minigraph_bgp_asn }} router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }}
bgp log-neighbor-changes bgp log-neighbor-changes
bgp bestpath as-path multipath-relax bgp bestpath as-path multipath-relax
{# TODO: use lo[0] for backward compatibility, will revisit the case with multiple lo interfaces #} {# TODO: use lo[0] for backward compatibility, will revisit the case with multiple lo interfaces #}
@ -46,16 +46,16 @@ router bgp {{ minigraph_bgp_asn }}
{% endfor %} {% endfor %}
{% endblock vlan_advertisement %} {% endblock vlan_advertisement %}
{% block bgp_sessions %} {% block bgp_sessions %}
{% for bgp_session in minigraph_bgp %} {% for neighbor_addr, bgp_session in BGP_NEIGHBOR.iteritems() %}
{% if bgp_session['asn'] != 0 %} {% if bgp_session['asn'] != 0 %}
neighbor {{ bgp_session['addr'] }} remote-as {{ bgp_session['asn'] }} neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }}
neighbor {{ bgp_session['addr'] }} description {{ bgp_session['name'] }} neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }}
{% if minigraph_devices[inventory_hostname]['type'] == 'ToRRouter' %} {% if minigraph_devices[inventory_hostname]['type'] == 'ToRRouter' %}
neighbor {{ bgp_session['addr'] }} allowas-in 1 neighbor {{ neighbor_addr }} allowas-in 1
{% endif %} {% endif %}
{% if bgp_session['addr'] | ipv6 %} {% if neighbor_addr | ipv6 %}
address-family ipv6 address-family ipv6
neighbor {{ bgp_session['addr'] }} activate neighbor {{ neighbor_addr }} activate
maximum-paths 64 maximum-paths 64
exit-address-family exit-address-family
{% endif %} {% endif %}
@ -66,5 +66,5 @@ router bgp {{ minigraph_bgp_asn }}
maximum-paths 64 maximum-paths 64
! !
route-map ISOLATE permit 10 route-map ISOLATE permit 10
set as-path prepend {{ minigraph_bgp_asn }} set as-path prepend {{ DEVICE_METADATA['localhost']['bgp_asn'] }}
! !

View File

@ -8,13 +8,13 @@ exit $?
## vtysh script start from next line, which line number MUST eqaul in 'sed' command above ## vtysh script start from next line, which line number MUST eqaul in 'sed' command above
configure terminal configure terminal
router bgp {{ minigraph_bgp_asn }} router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }}
{% for bgp_session in minigraph_bgp %} {% for neighbor_addr in BGP_NEIGHBOR %}
neighbor {{ bgp_session['addr'] }} route-map ISOLATE out neighbor {{ neighbor_addr }} route-map ISOLATE out
{% endfor %} {% endfor %}
exit exit
exit exit
{% for bgp_session in minigraph_bgp %} {% for neighbor_addr in BGP_NEIGHBOR %}
clear ip bgp {{ bgp_session['addr'] }} soft out clear ip bgp {{ neighbor_addr }} soft out
{% endfor %} {% endfor %}

View File

@ -8,13 +8,13 @@ exit $?
## vtysh script start from next line, which line number MUST eqaul in 'sed' command above ## vtysh script start from next line, which line number MUST eqaul in 'sed' command above
configure terminal configure terminal
router bgp {{ minigraph_bgp_asn }} router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }}
{% for bgp_session in minigraph_bgp %} {% for neighbor_ip in BGP_NEIGHBOR %}
no neighbor {{ bgp_session['addr'] }} route-map ISOLATE out no neighbor {{ neighbor_ip }} route-map ISOLATE out
{% endfor %} {% endfor %}
exit exit
exit exit
{% for bgp_session in minigraph_bgp %} {% for neighbor_ip in BGP_NEIGHBOR %}
clear ip bgp {{ bgp_session['addr'] }} soft out clear ip bgp {{ neighbor_ip }} soft out
{% endfor %} {% endfor %}

View File

@ -1,17 +1,17 @@
[global.config] [global.config]
as = {{ minigraph_bgp_asn }} as = {{ DEVICE_METADATA['localhost']['bgp_asn'] }}
router-id = "{{ minigraph_lo_interfaces[0]['addr'] }}" router-id = "{{ minigraph_lo_interfaces[0]['addr'] }}"
{% for bgp_session in minigraph_bgp %} {% for neighbor_addr, bgp_session in BGP_NEIGHBOR.iteritems() %}
{% if bgp_session['asn'] != 0 %} {% if bgp_session['asn'] != 0 %}
[[neighbors]] [[neighbors]]
[neighbors.config] [neighbors.config]
peer-as = {{ bgp_session['asn'] }} peer-as = {{ bgp_session['asn'] }}
neighbor-address = "{{ bgp_session['addr'] }}" neighbor-address = "{{ neighbor_addr }}"
[neighbors.graceful-restart.config] [neighbors.graceful-restart.config]
enabled = true enabled = true
[[neighbors.afi-safis]] [[neighbors.afi-safis]]
[neighbors.afi-safis.config] [neighbors.afi-safis.config]
{% if bgp_session['addr'] | ipv6 %} {% if neighbor_addr | ipv6 %}
afi-safi-name = "ipv6-unicast" afi-safi-name = "ipv6-unicast"
{% else %} {% else %}
afi-safi-name = "ipv4-unicast" afi-safi-name = "ipv4-unicast"

View File

@ -2,23 +2,3 @@
echo Not implemented yet echo Not implemented yet
exit exit
## vtysh only accepts script in stdin, so cannot be directly used in shebang
## Cut the tail of this script and feed vtysh stdin
sed -n -e '9,$p' < "$0" | vtysh "$@"
## Exit with vtysh return code
exit $?
## vtysh script start from next line, which line number MUST eqaul in 'sed' command above
configure terminal
router bgp {{ minigraph_bgp_asn }}
{% for bgp_session in minigraph_bgp %}
neighbor {{ bgp_session['addr'] }} route-map ISOLATE out
{% endfor %}
exit
exit
{% for bgp_session in minigraph_bgp %}
clear ip bgp {{ bgp_session['addr'] }} soft out
{% endfor %}

View File

@ -3,22 +3,3 @@
echo Not implemented yet echo Not implemented yet
exit exit
## vtysh only accepts script in stdin, so cannot be directly used in shebang
## Cut the tail of this script and feed vtysh stdin
sed -n -e '9,$p' < "$0" | vtysh "$@"
## Exit with vtysh return code
exit $?
## vtysh script start from next line, which line number MUST eqaul in 'sed' command above
configure terminal
router bgp {{ minigraph_bgp_asn }}
{% for bgp_session in minigraph_bgp %}
no neighbor {{ bgp_session['addr'] }} route-map ISOLATE out
{% endfor %}
exit
exit
{% for bgp_session in minigraph_bgp %}
clear ip bgp {{ bgp_session['addr'] }} soft out
{% endfor %}

View File

@ -6,37 +6,60 @@ import subprocess
import syslog import syslog
from swsssdk import ConfigDBConnector from swsssdk import ConfigDBConnector
# Returns BGP ASN as a string class BGPConfigDaemon:
def _get_bgp_asn_from_minigraph():
# Get BGP ASN from minigraph
proc = subprocess.Popen(
['sonic-cfggen', '-m', '/etc/sonic/minigraph.xml', '-v', 'minigraph_bgp_asn'],
stdout=subprocess.PIPE,
shell=False,
stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
return stdout.rstrip('\n')
def bgp_config(asn, ip, config): def __init__(self):
syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(ip, config)) self.config_db = ConfigDBConnector()
# Currently dynamic config is supported only for bgp admin status self.config_db.connect()
if config.has_key('admin_status'): self.bgp_asn = self.config_db.get_entry('DEVICE_METADATA', 'localhost')['bgp_asn']
command_mod = 'no ' if config['admin_status'] == 'up' else '' self.bgp_neighbor = self.config_db.get_table('BGP_NEIGHBOR')
command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c '{}neighbor {} shutdown'".format(asn, command_mod, ip)
def __run_command(self, command):
# print command
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
stdout = p.communicate()[0] stdout = p.communicate()[0]
p.wait() p.wait()
if p.returncode != 0: if p.returncode != 0:
syslog.syslog(syslog.LOG_ERR, '[bgp cfgd] command execution returned {}. Command: "{}", stdout: "{}"'.format(p.returncode, command, stdout)) syslog.syslog(syslog.LOG_ERR, '[bgp cfgd] command execution returned {}. Command: "{}", stdout: "{}"'.format(p.returncode, command, stdout))
def main(): def metadata_handler(self, key, data):
sub = ConfigDBConnector() if key == 'localhost' and data.has_key('bgp_asn'):
bgp_asn = _get_bgp_asn_from_minigraph() if data['bgp_asn'] != self.bgp_asn:
handler = lambda table, key, data: bgp_config(bgp_asn, key, data) syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] ASN changed to {} from {}, restart BGP...'.format(data['bgp_asn'], self.bgp_asn))
sub.subscribe('BGP_NEIGHBOR', handler) self.__run_command("supervisorctl restart start.sh")
sub.connect() self.__run_command("service quagga restart")
sub.listen() self.bgp_asn = data['bgp_asn']
def bgp_handler(self, key, data):
syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data))
if not data:
# 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)
else:
command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} remote-as {}'".format(self.bgp_asn, key, data['asn'])
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'])
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)
self.__run_command(command)
self.bgp_neighbor[key] = data
def start(self):
self.config_db.subscribe('BGP_NEIGHBOR',
lambda table, key, data: self.bgp_handler(key, data))
self.config_db.subscribe('DEVICE_METADATA',
lambda table, key, data: self.metadata_handler(key, data))
self.config_db.listen()
def main():
daemon = BGPConfigDaemon()
daemon.start()
if __name__ == "__main__":
main() main()

View File

@ -14,7 +14,7 @@ log facility local4
! enable password {# {{ en_passwd }} TODO: param needed #} ! enable password {# {{ en_passwd }} TODO: param needed #}
{% endblock system_init %} {% endblock system_init %}
! !
{% if minigraph_bgp_asn is not none %} {% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %}
{% block bgp_init %} {% block bgp_init %}
! !
! bgp multiple-instance ! bgp multiple-instance
@ -23,7 +23,7 @@ route-map FROM_BGP_SPEAKER_V4 permit 10
! !
route-map TO_BGP_SPEAKER_V4 deny 10 route-map TO_BGP_SPEAKER_V4 deny 10
! !
router bgp {{ minigraph_bgp_asn }} router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }}
bgp log-neighbor-changes bgp log-neighbor-changes
bgp bestpath as-path multipath-relax bgp bestpath as-path multipath-relax
{# Advertise graceful restart capability for ToR #} {# Advertise graceful restart capability for ToR #}
@ -50,24 +50,24 @@ router bgp {{ minigraph_bgp_asn }}
{% endfor %} {% endfor %}
{% endblock vlan_advertisement %} {% endblock vlan_advertisement %}
{% block bgp_sessions %} {% block bgp_sessions %}
{% for bgp_session in minigraph_bgp %} {% for neighbor_addr, bgp_session in BGP_NEIGHBOR.iteritems() %}
{% if bgp_session['asn'] != 0 %} {% if bgp_session['asn'] != 0 %}
neighbor {{ bgp_session['addr'] }} remote-as {{ bgp_session['asn'] }} neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }}
neighbor {{ bgp_session['addr'] }} description {{ bgp_session['name'] }} neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }}
{% if bgp_admin_state and bgp_admin_state.has_key(bgp_session['addr']) and bgp_admin_state[bgp_session['addr']]==False or bgp_admin_state and not bgp_admin_state.has_key(bgp_session['addr']) and bgp_admin_state.has_key('all') and bgp_admin_state['all']==False %} {% 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 {{ bgp_session['addr'] }} shutdown neighbor {{ neighbor_addr }} shutdown
{% endif %} {% endif %}
{% if bgp_session['addr'] | ipv4 %} {% if neighbor_addr | ipv4 %}
{% if minigraph_devices[inventory_hostname]['type'] == 'ToRRouter' %} {% if minigraph_devices[inventory_hostname]['type'] == 'ToRRouter' %}
neighbor {{ bgp_session['addr'] }} allowas-in 1 neighbor {{ neighbor_addr }} allowas-in 1
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if bgp_session['addr'] | ipv6 %} {% if neighbor_addr | ipv6 %}
address-family ipv6 address-family ipv6
{% if minigraph_devices[inventory_hostname]['type'] == 'ToRRouter' %} {% if minigraph_devices[inventory_hostname]['type'] == 'ToRRouter' %}
neighbor {{ bgp_session['addr'] }} allowas-in 1 neighbor {{ neighbor_addr }} allowas-in 1
{% endif %} {% endif %}
neighbor {{ bgp_session['addr'] }} activate neighbor {{ neighbor_addr }} activate
maximum-paths 64 maximum-paths 64
exit-address-family exit-address-family
{% endif %} {% endif %}
@ -75,7 +75,7 @@ router bgp {{ minigraph_bgp_asn }}
{% endfor %} {% endfor %}
{% endblock bgp_sessions %} {% endblock bgp_sessions %}
{% block bgp_peers_with_range %} {% block bgp_peers_with_range %}
{% for bgp_peer in minigraph_bgp_peers_with_range %} {% for bgp_peer in BGP_PEER_RANGE.values() %}
neighbor {{ bgp_peer['name'] }} peer-group neighbor {{ bgp_peer['name'] }} peer-group
neighbor {{ bgp_peer['name'] }} passive neighbor {{ bgp_peer['name'] }} passive
neighbor {{ bgp_peer['name'] }} remote-as {{deployment_id_asn_map[deployment_id] }} neighbor {{ bgp_peer['name'] }} remote-as {{deployment_id_asn_map[deployment_id] }}
@ -90,10 +90,10 @@ router bgp {{ minigraph_bgp_asn }}
{% endfor %} {% endfor %}
{% endblock bgp_peers_with_range %} {% endblock bgp_peers_with_range %}
! !
{% if minigraph_bgp_asn is not none %} {% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %}
maximum-paths 64 maximum-paths 64
! !
route-map ISOLATE permit 10 route-map ISOLATE permit 10
set as-path prepend {{ minigraph_bgp_asn }} set as-path prepend {{ DEVICE_METADATA['localhost']['bgp_asn'] }}
{% endif %} {% endif %}
! !

View File

@ -8,13 +8,13 @@ exit $?
## vtysh script start from next line, which line number MUST eqaul in 'sed' command above ## vtysh script start from next line, which line number MUST eqaul in 'sed' command above
configure terminal configure terminal
router bgp {{ minigraph_bgp_asn }} router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }}
{% for bgp_session in minigraph_bgp %} {% for neighbor_addr in BGP_NEIGHBOR %}
neighbor {{ bgp_session['addr'] }} route-map ISOLATE out neighbor {{ neighbor_addr }} route-map ISOLATE out
{% endfor %} {% endfor %}
exit exit
exit exit
{% for bgp_session in minigraph_bgp %} {% for neighbor_addr in BGP_NEIGHBOR %}
clear ip bgp {{ bgp_session['addr'] }} soft out clear ip bgp {{ neighbor_addr }} soft out
{% endfor %} {% endfor %}

View File

@ -3,13 +3,13 @@
mkdir -p /etc/quagga mkdir -p /etc/quagga
sonic-cfggen -m -d -y /etc/sonic/deployment_id_asn_map.yml -t /usr/share/sonic/templates/bgpd.conf.j2 > /etc/quagga/bgpd.conf sonic-cfggen -m -d -y /etc/sonic/deployment_id_asn_map.yml -t /usr/share/sonic/templates/bgpd.conf.j2 > /etc/quagga/bgpd.conf
sonic-cfggen -m /etc/sonic/minigraph.xml -t /usr/share/sonic/templates/zebra.conf.j2 > /etc/quagga/zebra.conf sonic-cfggen -m -d -t /usr/share/sonic/templates/zebra.conf.j2 > /etc/quagga/zebra.conf
sonic-cfggen -m /etc/sonic/minigraph.xml -t /usr/share/sonic/templates/isolate.j2 > /usr/sbin/bgp-isolate sonic-cfggen -m -d -t /usr/share/sonic/templates/isolate.j2 > /usr/sbin/bgp-isolate
chown root:root /usr/sbin/bgp-isolate chown root:root /usr/sbin/bgp-isolate
chmod 0755 /usr/sbin/bgp-isolate chmod 0755 /usr/sbin/bgp-isolate
sonic-cfggen -m /etc/sonic/minigraph.xml -t /usr/share/sonic/templates/unisolate.j2 > /usr/sbin/bgp-unisolate sonic-cfggen -m -d -t /usr/share/sonic/templates/unisolate.j2 > /usr/sbin/bgp-unisolate
chown root:root /usr/sbin/bgp-unisolate chown root:root /usr/sbin/bgp-unisolate
chmod 0755 /usr/sbin/bgp-unisolate chmod 0755 /usr/sbin/bgp-unisolate

View File

@ -8,13 +8,13 @@ exit $?
## vtysh script start from next line, which line number MUST eqaul in 'sed' command above ## vtysh script start from next line, which line number MUST eqaul in 'sed' command above
configure terminal configure terminal
router bgp {{ minigraph_bgp_asn }} router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }}
{% for bgp_session in minigraph_bgp %} {% for neighbor_ip in BGP_NEIGHBOR %}
no neighbor {{ bgp_session['addr'] }} route-map ISOLATE out no neighbor {{ neighbor_ip }} route-map ISOLATE out
{% endfor %} {% endfor %}
exit exit
exit exit
{% for bgp_session in minigraph_bgp %} {% for neighbor_ip in BGP_NEIGHBOR %}
clear ip bgp {{ bgp_session['addr'] }} soft out clear ip bgp {{ neighbor_ip }} soft out
{% endfor %} {% endfor %}

View File

@ -132,7 +132,7 @@ sudo bash -c "echo dhcp_as_static=true >> $FILESYSTEM_ROOT/etc/sonic/updategraph
sudo bash -c "echo enabled=false > $FILESYSTEM_ROOT/etc/sonic/updategraph.conf" sudo bash -c "echo enabled=false > $FILESYSTEM_ROOT/etc/sonic/updategraph.conf"
{% endif %} {% endif %}
{% if shutdown_bgp_on_start == "y" %} {% if shutdown_bgp_on_start == "y" %}
sudo bash -c "echo '{ \"bgp_admin_state\": { \"all\": false } }' >> $FILESYSTEM_ROOT/etc/sonic/config_db.json" sudo bash -c "echo '{ \"DEVICE_METADATA\": { \"localhost\": { \"default_bgp_status\": \"down\" } } }' >> $FILESYSTEM_ROOT/etc/sonic/init_cfg.json"
{% endif %} {% endif %}
# Copy SNMP configuration files # Copy SNMP configuration files
sudo cp $IMAGE_CONFIGS/snmp/snmp.yml $FILESYSTEM_ROOT/etc/sonic/ sudo cp $IMAGE_CONFIGS/snmp/snmp.yml $FILESYSTEM_ROOT/etc/sonic/

View File

@ -35,8 +35,20 @@ if [ -f /host/image-$sonic_version/platform/firsttime ]; then
mv -f /host/old_config/* /etc/sonic/ mv -f /host/old_config/* /etc/sonic/
elif [ -f /host/minigraph.xml ]; then elif [ -f /host/minigraph.xml ]; then
mv /host/minigraph.xml /etc/sonic/ mv /host/minigraph.xml /etc/sonic/
# Combine information in minigraph and init_cfg.json to form initiate config DB dump file.
# TODO: After moving all information from minigraph to DB, sample config DB dump should be provide
if [ -f /etc/sonic/init_cfg.json ]; then
sonic-cfggen -m -j /etc/sonic/init_cfg.json --print-data > /etc/sonic/config_db.json
else
sonic-cfggen -m --print-data > /etc/sonic/config_db.json
fi
else else
cp /usr/share/sonic/device/$platform/minigraph.xml /etc/sonic/ cp /usr/share/sonic/device/$platform/minigraph.xml /etc/sonic/
if [ -f /etc/sonic/init_cfg.json ]; then
sonic-cfggen -m -j /etc/sonic/init_cfg.json --print-data > /etc/sonic/config_db.json
else
sonic-cfggen -m --print-data > /etc/sonic/config_db.json
fi
fi fi
if [ -d /host/image-$sonic_version/platform/$platform ]; then if [ -d /host/image-$sonic_version/platform/$platform ]; then

View File

@ -68,6 +68,13 @@ while true; do
sleep 5 sleep 5
done done
echo "Regenerating config DB from minigraph..."
if [ -f /etc/sonic/init_cfg.json ]; then
sonic-cfggen -m -j /etc/sonic/init_cfg.json --print-data > /etc/sonic/config_db.json
else
sonic-cfggen -m --print-data > /etc/sonic/config_db.json
fi
# Mark as disabled after graph is successfully downloaded # Mark as disabled after graph is successfully downloaded
sed -i "/enabled=/d" /etc/sonic/updategraph.conf sed -i "/enabled=/d" /etc/sonic/updategraph.conf
echo "enabled=false" >> /etc/sonic/updategraph.conf echo "enabled=false" >> /etc/sonic/updategraph.conf

View File

@ -249,9 +249,9 @@ def parse_dpg(dpg, hname):
def parse_cpg(cpg, hname): def parse_cpg(cpg, hname):
bgp_sessions = [] bgp_sessions = {}
myasn = None myasn = None
bgp_peers_with_range = [] bgp_peers_with_range = {}
for child in cpg: for child in cpg:
tag = child.tag tag = child.tag
if tag == str(QName(ns, "PeeringSessions")): if tag == str(QName(ns, "PeeringSessions")):
@ -261,17 +261,15 @@ def parse_cpg(cpg, hname):
end_router = session.find(str(QName(ns, "EndRouter"))).text end_router = session.find(str(QName(ns, "EndRouter"))).text
end_peer = session.find(str(QName(ns, "EndPeer"))).text end_peer = session.find(str(QName(ns, "EndPeer"))).text
if end_router == hname: if end_router == hname:
bgp_sessions.append({ bgp_sessions[start_peer] = {
'name': start_router, 'name': start_router,
'addr': start_peer, 'local_addr': end_peer
'peer_addr': end_peer }
})
else: else:
bgp_sessions.append({ bgp_sessions[end_peer] = {
'name': end_router, 'name': end_router,
'addr': end_peer, 'local_addr': start_peer
'peer_addr': start_peer }
})
elif child.tag == str(QName(ns, "Routers")): elif child.tag == str(QName(ns, "Routers")):
for router in child.findall(str(QName(ns1, "BGPRouterDeclaration"))): for router in child.findall(str(QName(ns1, "BGPRouterDeclaration"))):
asn = router.find(str(QName(ns1, "ASN"))).text asn = router.find(str(QName(ns1, "ASN"))).text
@ -285,12 +283,13 @@ def parse_cpg(cpg, hname):
name = bgpPeer.find(str(QName(ns1, "Name"))).text name = bgpPeer.find(str(QName(ns1, "Name"))).text
ip_range = bgpPeer.find(str(QName(ns1, "PeersRange"))).text ip_range = bgpPeer.find(str(QName(ns1, "PeersRange"))).text
ip_range_group = ip_range.split(';') if ip_range and ip_range != "" else [] ip_range_group = ip_range.split(';') if ip_range and ip_range != "" else []
bgp_peers_with_range.append({ bgp_peers_with_range[name] = {
'name': name, 'name': name,
'ip_range': ip_range_group 'ip_range': ip_range_group
}) }
else: else:
for bgp_session in bgp_sessions: for peer in bgp_sessions:
bgp_session = bgp_sessions[peer]
if hostname == bgp_session['name']: if hostname == bgp_session['name']:
bgp_session['asn'] = int(asn) bgp_session['asn'] = int(asn)
@ -447,9 +446,9 @@ def parse_xml(filename, platform=None, port_config_file=None):
# sorting by lambdas are not easily done without custom filters. # sorting by lambdas are not easily done without custom filters.
# TODO: add jinja2 filter to accept a lambda to sort a list of dictionaries by attribute. # TODO: add jinja2 filter to accept a lambda to sort a list of dictionaries by attribute.
# TODO: alternatively (preferred), implement class containers for multiple-attribute entries, enabling sort by attr # TODO: alternatively (preferred), implement class containers for multiple-attribute entries, enabling sort by attr
results['minigraph_bgp'] = sorted(bgp_sessions, key=lambda x: x['addr']) results['BGP_NEIGHBOR'] = bgp_sessions
results['minigraph_bgp_asn'] = bgp_asn results['DEVICE_METADATA'] = {'localhost': { 'bgp_asn': bgp_asn }}
results['minigraph_bgp_peers_with_range'] = bgp_peers_with_range results['BGP_PEER_RANGE'] = bgp_peers_with_range
# TODO: sort does not work properly on all interfaces of varying lengths. Need to sort by integer group(s). # TODO: sort does not work properly on all interfaces of varying lengths. Need to sort by integer group(s).
phyport_intfs = [] phyport_intfs = []

View File

@ -73,37 +73,25 @@ TODO(taoyl): Current version of config db only supports BGP admin states.
""" """
@staticmethod @staticmethod
def db_to_output(db_data): def db_to_output(db_data):
data_bgp_admin = {} return db_data
for table_name, content in db_data.iteritems():
if table_name == 'BGP_NEIGHBOR':
for key, value in content.iteritems():
if value.has_key('admin_status'):
data_bgp_admin[key] = (value['admin_status'] == 'up')
elif table_name == 'DEVICE_METADATA':
if content['localhost'].has_key('bgp_default_status'):
data_bgp_admin['all'] = (content['localhost']['bgp_default_status'] == 'up')
output_data = {'bgp_admin_state': data_bgp_admin} if data_bgp_admin else {}
return output_data
@staticmethod @staticmethod
def output_to_db(output_data): def output_to_db(output_data):
db_data = {} db_data = {}
for key, value in output_data.iteritems(): for table_name in output_data:
if key == 'bgp_admin_state': if table_name == 'BGP_NEIGHBOR' or table_name == 'BGP_PEER_RANGE' or table_name == 'DEVICE_METADATA':
for neighbor, state in value.iteritems(): db_data[table_name] = output_data[table_name]
if neighbor == 'all':
if not db_data.has_key('DEVICE_METADATA'):
db_data['DEVICE_METADATA'] = {'localhost': {}}
db_data['DEVICE_METADATA']['localhost']['bgp_default_status'] = 'up' if state else 'down'
else:
if not db_data.has_key('BGP_NEIGHBOR'):
db_data['BGP_NEIGHBOR'] = {}
if not db_data['BGP_NEIGHBOR'].has_key(neighbor):
db_data['BGP_NEIGHBOR'][neighbor] = {}
db_data['BGP_NEIGHBOR'][neighbor]['admin_status'] = 'up' if state else 'down'
return db_data return db_data
def deep_update(dst, src):
for key, value in src.iteritems():
if isinstance(value, dict):
node = dst.setdefault(key, {})
deep_update(node, value)
else:
dst[key] = value
return dst
def main(): def main():
parser=argparse.ArgumentParser(description="Render configuration file from minigraph data and jinja2 template.") parser=argparse.ArgumentParser(description="Render configuration file from minigraph data and jinja2 template.")
@ -126,7 +114,7 @@ def main():
data = {} data = {}
machine_info = get_machine_info() machine_info = get_machine_info()
if machine_info != None: if machine_info != None:
data.update(machine_info) deep_update(data, machine_info)
platform_info = get_platform_info(machine_info) platform_info = get_platform_info(machine_info)
if platform_info != None: if platform_info != None:
data['platform'] = platform_info data['platform'] = platform_info
@ -135,34 +123,34 @@ def main():
minigraph = args.minigraph minigraph = args.minigraph
if data.has_key('platform'): if data.has_key('platform'):
if args.port_config != None: if args.port_config != None:
data.update(parse_xml(minigraph, data['platform'], args.port_config)) deep_update(data, parse_xml(minigraph, data['platform'], args.port_config))
else: else:
data.update(parse_xml(minigraph, data['platform'])) deep_update(data, parse_xml(minigraph, data['platform']))
else: else:
if args.port_config != None: if args.port_config != None:
data.update(parse_xml(minigraph, port_config_file=args.port_config)) deep_update(data, parse_xml(minigraph, port_config_file=args.port_config))
else: else:
data.update(parse_xml(minigraph)) deep_update(data, parse_xml(minigraph))
if args.device_description != None: if args.device_description != None:
data.update(parse_device_desc_xml(args.device_description)) deep_update(data, parse_device_desc_xml(args.device_description))
for yaml_file in args.yaml: for yaml_file in args.yaml:
with open(yaml_file, 'r') as stream: with open(yaml_file, 'r') as stream:
additional_data = yaml.load(stream) additional_data = yaml.load(stream)
data.update(additional_data) deep_update(data, additional_data)
for json_file in args.json: for json_file in args.json:
with open(json_file, 'r') as stream: with open(json_file, 'r') as stream:
data.update(json.load(stream)) deep_update(data, json.load(stream))
if args.additional_data != None: if args.additional_data != None:
data.update(json.loads(args.additional_data)) deep_update(data, json.loads(args.additional_data))
if args.from_db: if args.from_db:
configdb = ConfigDBConnector() configdb = ConfigDBConnector()
configdb.connect() configdb.connect()
data.update(FormatConverter.db_to_output(configdb.get_config())) deep_update(data, FormatConverter.db_to_output(configdb.get_config()))
if args.template != None: if args.template != None:
template_file = os.path.abspath(args.template) template_file = os.path.abspath(args.template)

View File

@ -111,7 +111,7 @@ class TestCfgGen(TestCase):
self.assertEqual(output.strip(), "{'Ethernet116': {'name': 'ARISTA02T1', 'port': 'Ethernet1/1'}, 'Ethernet124': {'name': 'ARISTA04T1', 'port': 'Ethernet1/1'}, 'Ethernet112': {'name': 'ARISTA01T1', 'port': 'Ethernet1/1'}, 'Ethernet120': {'name': 'ARISTA03T1', 'port': 'Ethernet1/1'}}") self.assertEqual(output.strip(), "{'Ethernet116': {'name': 'ARISTA02T1', 'port': 'Ethernet1/1'}, 'Ethernet124': {'name': 'ARISTA04T1', 'port': 'Ethernet1/1'}, 'Ethernet112': {'name': 'ARISTA01T1', 'port': 'Ethernet1/1'}, 'Ethernet120': {'name': 'ARISTA03T1', 'port': 'Ethernet1/1'}}")
def test_minigraph_peers_with_range(self): def test_minigraph_peers_with_range(self):
argument = '-m "' + self.sample_graph_bgp_speaker + '" -p "' + self.port_config + '" -v minigraph_bgp_peers_with_range' argument = '-m "' + self.sample_graph_bgp_speaker + '" -p "' + self.port_config + '" -v BGP_PEER_RANGE.values\(\)'
output = self.run_script(argument) output = self.run_script(argument)
self.assertEqual(output.strip(), "[{'name': 'BGPSLBPassive', 'ip_range': ['10.10.10.10/26', '100.100.100.100/26']}]") self.assertEqual(output.strip(), "[{'name': 'BGPSLBPassive', 'ip_range': ['10.10.10.10/26', '100.100.100.100/26']}]")

@ -1 +1 @@
Subproject commit 6c7e22362fbc05ba455e7e336e2a88430de0de18 Subproject commit e4f7161b055a345813424004f17b116a055d590a