From fe497a93b4e78a2ba6d0b263240a348b094f8405 Mon Sep 17 00:00:00 2001 From: Taoyu Li Date: Thu, 29 Dec 2016 19:51:38 +0000 Subject: [PATCH] Let bgp docker generate configuration by itself --- dockers/docker-bgp/Dockerfile | 4 + dockers/docker-bgp/bgpd.conf.j2 | 63 ++++ dockers/docker-bgp/config.sh | 6 + dockers/docker-bgp/isolate.j2 | 20 ++ dockers/docker-bgp/minigraph_facts.py | 418 ++++++++++++++++++++++++++ dockers/docker-bgp/render_config.py | 58 ++++ dockers/docker-bgp/template_list | 4 + dockers/docker-bgp/unisolate.j2 | 20 ++ dockers/docker-bgp/zebra.conf.j2 | 61 ++++ 9 files changed, 654 insertions(+) create mode 100644 dockers/docker-bgp/bgpd.conf.j2 create mode 100755 dockers/docker-bgp/config.sh create mode 100755 dockers/docker-bgp/isolate.j2 create mode 100644 dockers/docker-bgp/minigraph_facts.py create mode 100644 dockers/docker-bgp/render_config.py create mode 100644 dockers/docker-bgp/template_list create mode 100755 dockers/docker-bgp/unisolate.j2 create mode 100644 dockers/docker-bgp/zebra.conf.j2 diff --git a/dockers/docker-bgp/Dockerfile b/dockers/docker-bgp/Dockerfile index b01698e31118..66a942f8d14f 100755 --- a/dockers/docker-bgp/Dockerfile +++ b/dockers/docker-bgp/Dockerfile @@ -3,11 +3,15 @@ FROM docker-base COPY deps/quagga_*.deb /deps/ RUN dpkg_apt() { [ -f $1 ] && { dpkg -i $1 || apt-get -y install -f; } || return 1; } && \ dpkg_apt /deps/quagga_*.deb && \ + apt-get update && \ + apt-get -y install -f python-jinja2 python-netaddr python-ipaddr python-lxml && \ apt-get clean -y && apt-get autoclean -y && apt-get autoremove -y && \ rm -rf /deps COPY daemons /etc/quagga/ +COPY ["*.j2", "config.sh", "template_list", "*.py", "/etc/swss/"] ENTRYPOINT service rsyslog start \ + && /etc/swss/config.sh \ && service quagga start \ && /bin/bash diff --git a/dockers/docker-bgp/bgpd.conf.j2 b/dockers/docker-bgp/bgpd.conf.j2 new file mode 100644 index 000000000000..4eea3d480a85 --- /dev/null +++ b/dockers/docker-bgp/bgpd.conf.j2 @@ -0,0 +1,63 @@ +! +{% block banner %} +! =========== Managed by Ansible DO NOT EDIT! ======================== +! generated by templates/quagga/bgpd.conf.j2 using minigraph_facts.py +! file: bgpd.conf +! +{% endblock banner %} +! +{% block system_init %} +hostname {{ inventory_hostname }} +password zebra +log syslog informational +log facility local4 +! enable password {# {{ en_passwd }} TODO: param needed #} +{% endblock system_init %} +! +{% block bgp_init %} +! +! bgp multiple-instance +! +router bgp {{ minigraph_bgp_asn }} + bgp log-neighbor-changes + bgp bestpath as-path multipath-relax +{# TODO: use lo[0] for backward compatibility, will revisit the case with multiple lo interfaces #} + bgp router-id {{ minigraph_lo_interfaces[0]['addr'] }} +{# advertise loopback #} +{% for lo in minigraph_lo_interfaces %} +{% if lo['addr'] | ipv4 %} + network {{ lo['addr'] }}/32 +{% elif lo['addr'] | ipv6 %} + address-family ipv6 + network {{ lo['addr'] }}/128 + exit-address-family +{% endif %} +{% endfor %} +{% endblock bgp_init %} +{% block vlan_advertisement %} +{% for interface in minigraph_interfaces %} +{% if interface['name'].startswith('Vlan') %} + network {{ interface['subnet'] }} +{% endif %} +{% endfor %} +{% endblock vlan_advertisement %} +{% block bgp_sessions %} +{% for bgp_session in minigraph_bgp %} +{% if bgp_session['asn'] != 0 %} + neighbor {{ bgp_session['addr'] }} remote-as {{ bgp_session['asn'] }} + neighbor {{ bgp_session['addr'] }} description {{ bgp_session['name'] }} +{% if bgp_session['addr'] | ipv6 %} + address-family ipv6 + neighbor {{ bgp_session['addr'] }} activate + maximum-paths 64 + exit-address-family +{% endif %} +{% endif %} +{% endfor %} +{% endblock bgp_sessions %} +! +maximum-paths 64 +! +route-map ISOLATE permit 10 +set as-path prepend {{ minigraph_bgp_asn }} +! diff --git a/dockers/docker-bgp/config.sh b/dockers/docker-bgp/config.sh new file mode 100755 index 000000000000..de6d0d69da02 --- /dev/null +++ b/dockers/docker-bgp/config.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +cd /etc/swss + +awk '{system("python render_config.py -m /etc/swss/minigraph.xml "$1 ">"$2" && chown "$3" "$2" && chmod "$4" "$2)}' template_list + diff --git a/dockers/docker-bgp/isolate.j2 b/dockers/docker-bgp/isolate.j2 new file mode 100755 index 000000000000..35ef5bbc0209 --- /dev/null +++ b/dockers/docker-bgp/isolate.j2 @@ -0,0 +1,20 @@ +#!/bin/bash +## 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 %} diff --git a/dockers/docker-bgp/minigraph_facts.py b/dockers/docker-bgp/minigraph_facts.py new file mode 100644 index 000000000000..a87493334813 --- /dev/null +++ b/dockers/docker-bgp/minigraph_facts.py @@ -0,0 +1,418 @@ +#!/usr/bin/env python +import calendar +import os +import sys +import socket +import struct +import json +import copy +import ipaddr as ipaddress +from collections import defaultdict + +from lxml import etree as ET +from lxml.etree import QName + +DOCUMENTATION = ''' +--- +module: minigraph_facts +version_added: "1.9" +author: Guohan Lu (gulv@microsoft.com) +short_description: Retrive minigraph facts for a device. +description: + - Retrieve minigraph facts for a device, the facts will be + inserted to the ansible_facts key. +options: + host: + description: + - Set to target snmp server (normally {{inventory_hostname}}) + required: true +''' + +EXAMPLES = ''' +# Gather minigraph facts +- name: Gathering minigraph facts about the device + minigraph_facts: host={{ hostname }} +''' + +ns = "Microsoft.Search.Autopilot.Evolution" +ns1 = "http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution" +ns2 = "Microsoft.Search.Autopilot.NetMux" +ns3 = "http://www.w3.org/2001/XMLSchema-instance" + +ANSIBLE_USER_MINIGRAPH_PATH = os.path.expanduser('~/.ansible/minigraph') +ANSIBLE_LOCAL_MINIGRAPH_PATH = 'minigraph/{}.xml' +ANSIBLE_USER_MINIGRAPH_MAX_AGE = 86400 # 24-hours (in seconds) + +class minigraph_encoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, (ipaddress.IPv4Network, ipaddress.IPv6Network, ipaddress.IPv4Address, ipaddress.IPv6Address)): + return str(obj) + return json.JSONEncoder.default(self, obj) + +def parse_png(png, hname): + neighbors = {} + devices = {} + console_dev = '' + console_port = '' + mgmt_dev = '' + mgmt_port = '' + for child in png: + if child.tag == str(QName(ns, "DeviceInterfaceLinks")): + for link in child.findall(str(QName(ns, "DeviceLinkBase"))): + linktype = link.find(str(QName(ns, "ElementType"))).text + if linktype != "DeviceInterfaceLink" and linktype != "UnderlayInterfaceLink": + continue + + enddevice = link.find(str(QName(ns, "EndDevice"))).text + endport = link.find(str(QName(ns, "EndPort"))).text + startdevice = link.find(str(QName(ns, "StartDevice"))).text + startport = link.find(str(QName(ns, "StartPort"))).text + + if enddevice == hname: + neighbors[endport] = {'name': startdevice, 'port': startport} + else: + neighbors[startport] = {'name': enddevice, 'port': endport} + if child.tag == str(QName(ns, "Devices")): + for device in child.findall(str(QName(ns, "Device"))): + lo_addr = None + # don't shadow type() + d_type = None + mgmt_addr = None + hwsku = None + if str(QName(ns3, "type")) in device.attrib: + d_type = device.attrib[str(QName(ns3, "type"))] + + for node in device: + if node.tag == str(QName(ns, "Address")): + lo_addr = node.find(str(QName(ns2, "IPPrefix"))).text.split('/')[0] + elif node.tag == str(QName(ns, "ManagementAddress")): + mgmt_addr = node.find(str(QName(ns2, "IPPrefix"))).text.split('/')[0] + elif node.tag == str(QName(ns, "Hostname")): + name = node.text + elif node.tag == str(QName(ns, "HwSku")): + hwsku = node.text + + devices[name] = {'lo_addr': lo_addr, 'type': d_type, 'mgmt_addr': mgmt_addr, 'hwsku': hwsku} + + if child.tag == str(QName(ns, "DeviceInterfaceLinks")): + for if_link in child.findall(str(QName(ns, 'DeviceLinkBase'))): + if str(QName(ns3, "type")) in if_link.attrib: + link_type = if_link.attrib[str(QName(ns3, "type"))] + if link_type == 'DeviceSerialLink': + for node in if_link: + if node.tag == str(QName(ns, "EndPort")): + console_port = node.text.split()[-1] + elif node.tag == str(QName(ns, "EndDevice")): + console_dev = node.text + elif link_type == 'DeviceMgmtLink': + for node in if_link: + if node.tag == str(QName(ns, "EndPort")): + mgmt_port = node.text.split()[-1] + elif node.tag == str(QName(ns, "EndDevice")): + mgmt_dev = node.text + + + return (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port) + + +def parse_dpg(dpg, hname): + for child in dpg: + hostname = child.find(str(QName(ns, "Hostname"))) + if hostname.text != hname: + continue + + ipintfs = child.find(str(QName(ns, "IPInterfaces"))) + intfs = [] + vlan_map = {} + for ipintf in ipintfs.findall(str(QName(ns, "IPInterface"))): + intfname = ipintf.find(str(QName(ns, "AttachTo"))).text + ipprefix = ipintf.find(str(QName(ns, "Prefix"))).text + ipn = ipaddress.IPNetwork(ipprefix) + ipaddr = ipn.ip + prefix_len = ipn.prefixlen + addr_bits = ipn.max_prefixlen + subnet = ipaddress.IPNetwork(str(ipn.network) + '/' + str(prefix_len)) + ipmask = ipn.netmask + + intf = {'addr': ipaddr, 'subnet': subnet} + if isinstance(ipn, ipaddress.IPv4Network): + intf['mask'] = ipmask + else: + intf['mask'] = str(prefix_len) + + if intfname[0:4] == "Vlan": + if intfname in vlan_map: + vlan_map[intfname].append(intf) + + else: + vlan_map[intfname] = [intf] + else: + intf.update({'name': intfname, 'prefixlen': int(prefix_len)}) + + if port_alias_map.has_key(intfname): + intf['alias'] = port_alias_map[intfname] + else: + intf['alias'] = intfname + + # TODO: remove peer_addr after dependency removed + ipaddr_val = int(ipn.ip) + peer_addr_val = None + if int(prefix_len) == addr_bits - 2: + if ipaddr_val & 0x3 == 1: + peer_addr_val = ipaddr_val + 1 + else: + peer_addr_val = ipaddr_val - 1 + elif int(prefix_len) == addr_bits - 1: + if ipaddr_val & 0x1 == 0: + peer_addr_val = ipaddr_val + 1 + else: + peer_addr_val = ipaddr_val - 1 + + if peer_addr_val is not None: + intf['peer_addr'] = ipaddress.IPAddress(peer_addr_val) + intfs.append(intf) + + pcintfs = child.find(str(QName(ns, "PortChannelInterfaces"))) + pc_intfs = [] + for pcintf in pcintfs.findall(str(QName(ns, "PortChannel"))): + pcintfname = pcintf.find(str(QName(ns, "Name"))).text + pcintfmbr = pcintf.find(str(QName(ns, "AttachTo"))).text + mbr_list = pcintfmbr.split(';', 1) + pc_intfs.append({'name': pcintfname, 'members': mbr_list}) + + lointfs = child.find(str(QName(ns, "LoopbackIPInterfaces"))) + lo_intfs = [] + for lointf in lointfs.findall(str(QName(ns1, "LoopbackIPInterface"))): + intfname = lointf.find(str(QName(ns, "AttachTo"))).text + ipprefix = lointf.find(str(QName(ns1, "PrefixStr"))).text + ipn = ipaddress.IPNetwork(ipprefix) + ipaddr = ipn.ip + prefix_len = ipn.prefixlen + ipmask = ipn.netmask + lo_intf = {'name': intfname, 'addr': ipaddr, 'prefixlen': prefix_len} + if isinstance(ipn, ipaddress.IPv4Network): + lo_intf['mask'] = ipmask + else: + lo_intf['mask'] = str(prefix_len) + lo_intfs.append(lo_intf) + + mgmtintfs = child.find(str(QName(ns, "ManagementIPInterfaces"))) + mgmt_intf = None + for mgmtintf in mgmtintfs.findall(str(QName(ns1, "ManagementIPInterface"))): + ipprefix = mgmtintf.find(str(QName(ns1, "PrefixStr"))).text + mgmtipn = ipaddress.IPNetwork(ipprefix) + ipaddr = mgmtipn.ip + prefix_len = str(mgmtipn.prefixlen) + ipmask = mgmtipn.netmask + gwaddr = ipaddress.IPAddress(int(mgmtipn.network) + 1) + mgmt_intf = {'addr': ipaddr, 'prefixlen': prefix_len, 'mask': ipmask, 'gwaddr': gwaddr} + + vlanintfs = child.find(str(QName(ns, "VlanInterfaces"))) + vlan_intfs = [] + for vintf in vlanintfs.findall(str(QName(ns, "VlanInterface"))): + vintfname = vintf.find(str(QName(ns, "Name"))).text + vlanid = vintf.find(str(QName(ns, "VlanID"))).text + members = vintf.find(str(QName(ns, "AttachTo"))).text + members = " ".join(members.split(';')) + vlan_attributes = {'name': vintfname, 'members': members, 'vlanid': vlanid} + for addrtuple in vlan_map.get(vintfname, []): + vlan_attributes.update(addrtuple) + vlan_intfs.append(copy.deepcopy(vlan_attributes)) + + return intfs, lo_intfs, mgmt_intf, vlan_intfs, pc_intfs + return None, None, None, None, None + +def parse_cpg(cpg, hname): + bgp_sessions = [] + myasn = None + for child in cpg: + tag = child.tag + if tag == str(QName(ns, "PeeringSessions")): + for session in child.findall(str(QName(ns, "BGPSession"))): + start_router = session.find(str(QName(ns, "StartRouter"))).text + start_peer = session.find(str(QName(ns, "StartPeer"))).text + end_router = session.find(str(QName(ns, "EndRouter"))).text + end_peer = session.find(str(QName(ns, "EndPeer"))).text + if end_router == hname: + bgp_sessions.append({ + 'name': start_router, + 'addr': start_peer, + 'peer_addr': end_peer + }) + else: + bgp_sessions.append({ + 'name': end_router, + 'addr': end_peer, + 'peer_addr': start_peer + }) + elif child.tag == str(QName(ns, "Routers")): + for router in child.findall(str(QName(ns1, "BGPRouterDeclaration"))): + asn = router.find(str(QName(ns1, "ASN"))).text + hostname = router.find(str(QName(ns1, "Hostname"))).text + if hostname == hname: + myasn = int(asn) + else: + for bgp_session in bgp_sessions: + if hostname == bgp_session['name']: + bgp_session['asn'] = int(asn) + + return bgp_sessions, myasn + + +def get_console_info(devices, dev, port): + for k, v in devices.items(): + if k == dev: + break + else: + return {} + + ret_val = v + ret_val.update({ + 'ts_port': port, + 'ts_dev': dev + }) + + return ret_val + +def get_mgmt_info(devices, dev, port): + for k, v in devices.items(): + if k == dev: + break + else: + return {} + + ret_val = v + ret_val.update({ + 'mgmt_port': port, + 'mgmt_dev': dev + }) + + return ret_val + + +def file_age(filename): + """ + :param filename: The filename to carbon date. + :return: The age of the file in seconds. + """ + return calendar.timegm(time.gmtime()) - os.path.getctime(filename) + + +def reconcile_mini_graph_locations(filename, hostname): + """ + Location precedence: + 1. "filename" module parameter + 2. minigraph/ folder + 3. .ansible/minigraph/ folder (<24 hrs old) + 4. Network Graph Service + + post-NGS download, cache to the user folder: + ~/.ansible/minigraph/HOSTNAME_minigraph.xml + + :param filename: the filename to load (may be None) + :param hostname: the hostname to load (required) + :return: tuple(the absolute filepath of the {cached,loaded} mini-graph, the root node of the loaded graph) + """ + if filename is not None: + # literal filename specified. read directly from the file. + root = ET.parse(filename).getroot() + mini_graph_path = filename + else: + # only the hostname was specified, determine the output path + mini_graph_path = os.path.join(ANSIBLE_USER_MINIGRAPH_PATH, hostname + '_minigraph.xml') + if os.path.exists(mini_graph_path) and file_age(mini_graph_path) < ANSIBLE_USER_MINIGRAPH_MAX_AGE: + # found a cached mini-graph, load it. + root = ET.parse(mini_graph_path).getroot() + return mini_graph_path, root + + +def parse_xml(filename): + #mini_graph_path, root = reconcile_mini_graph_locations(filename, hostname) + root = ET.parse(filename).getroot() + mini_graph_path = filename + + u_neighbors = None + u_devices = None + hwsku = None + bgp_sessions = None + bgp_asn = None + intfs = None + vlan_intfs = None + pc_intfs = None + mgmt_intf = None + lo_intf = None + neighbors = None + devices = None + hostname = None + + hwsku_qn = QName(ns, "HwSku") + hostname_qn = QName(ns, "Hostname") + for child in root: + if child.tag == str(hwsku_qn): + hwsku = child.text + if child.tag == str(hostname_qn): + hostname = child.text + + # port_alias_map maps ngs port name to sonic port name + if hwsku == "Force10-S6000": + for i in range(0, 128, 4): + port_alias_map["fortyGigE0/%d" % i] = "Ethernet%d" % i + elif hwsku == "Arista-7050-QX32": + for i in range(1, 25): + port_alias_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4) + for i in range(25, 33): + port_alias_map["Ethernet%d" % i] = "Ethernet%d" % ((i - 1) * 4) + + for child in root: + if child.tag == str(QName(ns, "DpgDec")): + (intfs, lo_intfs, mgmt_intf, vlan_intfs, pc_intfs) = parse_dpg(child, hostname) + elif child.tag == str(QName(ns, "CpgDec")): + (bgp_sessions, bgp_asn) = parse_cpg(child, hostname) + elif child.tag == str(QName(ns, "PngDec")): + (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port) = parse_png(child, hostname) + elif child.tag == str(QName(ns, "UngDec")): + (u_neighbors, u_devices, _, _, _, _) = parse_png(child, hostname) + + # Replace port with alias in port channel interfaces members + for pc in pc_intfs: + for i,member in enumerate(pc['members']): + pc['members'][i] = port_alias_map[member] + + Tree = lambda: defaultdict(Tree) + + results = Tree() + results['minigraph_hwsku'] = hwsku + # 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: 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['minigraph_bgp_asn'] = bgp_asn + # TODO: sort does not work properly on all interfaces of varying lengths. Need to sort by integer group(s). + results['minigraph_interfaces'] = sorted(intfs, key=lambda x: x['name']) + results['minigraph_vlan_interfaces'] = vlan_intfs + results['minigraph_portchannel_interfaces'] = pc_intfs + results['minigraph_mgmt_interface'] = mgmt_intf + results['minigraph_lo_interfaces'] = lo_intfs + results['minigraph_neighbors'] = neighbors + results['minigraph_devices'] = devices + results['minigraph_underlay_neighbors'] = u_neighbors + results['minigraph_underlay_devices'] = u_devices + # note - this may include files under acs/ansible/minigraph, or those under the default cache folder. + # (see ANSIBLE_USER_MINIGRAPH_PATH at the top of the module) + results['minigraph_as_xml'] = mini_graph_path + results['minigraph_console'] = get_console_info(devices, console_dev, console_port) + results['minigraph_mgmt'] = get_mgmt_info(devices, mgmt_dev, mgmt_port) + results['inventory_hostname'] = hostname + + return results + + +port_alias_map = {} + + +def print_parse_xml(hostname): + filename = '../minigraph/' + hostname + '.xml' + results = parse_xml(filename, hostname) + print(json.dumps(results, indent=3, cls=minigraph_encoder)) + + diff --git a/dockers/docker-bgp/render_config.py b/dockers/docker-bgp/render_config.py new file mode 100644 index 000000000000..9c85a0f860e7 --- /dev/null +++ b/dockers/docker-bgp/render_config.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +import sys +import os.path +import argparse + +from minigraph_facts import parse_xml +import jinja2 +import netaddr + + +def is_ipv4(value): + if not value: + return False + if isinstance(value, netaddr.IPAddress): + addr = value + else: + try: + addr = netaddr.IPAddress(str(value)) + except: + return False + return addr.version == 4 + +def is_ipv6(value): + if not value: + return False + if isinstance(value, netaddr.IPAddress): + addr = value + else: + try: + addr = netaddr.IPAddress(str(value)) + except: + return False + return addr.version == 6 + + +def main(): + parser=argparse.ArgumentParser(description="Render configuration file from minigraph data and jinja2 template.") + parser.add_argument("template") + parser.add_argument("-m", "--minigraph", required=True, help="minigraph xml file") + #parser.add_argument("-v", "--variable-file", help="yaml file that contains addtional variables") + args = parser.parse_args() + + minigraph = args.minigraph + template_file = args.template + + data = parse_xml(minigraph) + + env = jinja2.Environment(loader=jinja2.FileSystemLoader('./'), trim_blocks=True) + env.filters['ipv4'] = is_ipv4 + env.filters['ipv6'] = is_ipv6 + template = env.get_template(template_file) + + print template.render(data) + +if __name__ == "__main__": + main() + diff --git a/dockers/docker-bgp/template_list b/dockers/docker-bgp/template_list new file mode 100644 index 000000000000..f031bea2467b --- /dev/null +++ b/dockers/docker-bgp/template_list @@ -0,0 +1,4 @@ +bgpd.conf.j2 /etc/quagga/bgpd.conf quagga:quaggavty 0644 +zebra.conf.j2 /etc/quagga/zebra.conf root:root 0644 +isolate.j2 /usr/sbin/bgp-isolate root:root 0755 +unisolate.j2 /usr/sbin/bgp-unisolate root:root 0755 diff --git a/dockers/docker-bgp/unisolate.j2 b/dockers/docker-bgp/unisolate.j2 new file mode 100755 index 000000000000..c113a74fab45 --- /dev/null +++ b/dockers/docker-bgp/unisolate.j2 @@ -0,0 +1,20 @@ +#!/bin/bash +## 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 %} diff --git a/dockers/docker-bgp/zebra.conf.j2 b/dockers/docker-bgp/zebra.conf.j2 new file mode 100644 index 000000000000..0a9001178750 --- /dev/null +++ b/dockers/docker-bgp/zebra.conf.j2 @@ -0,0 +1,61 @@ +! +{% block banner %} +! =========== Managed by Ansible DO NOT EDIT! ======================== +! generated by templates/quagga/zebra.conf.j2 using minigraph_facts.py +! file: zebra.conf +! +{% endblock banner %} +! +{% block sys_init %} +hostname {{ inventory_hostname }} +password zebra +enable password zebra +{% endblock sys_init %} +! +{% block interfaces %} +! Enable link-detect (default disabled) +{% for interface in minigraph_interfaces %} +interface {{ interface['alias'] }} +link-detect +! +{% endfor %} +{% endblock interfaces %} +! +{% block default_route %} +! set static default route to mgmt gateway as a backup to learned default +ip route 0.0.0.0/0 {{ minigraph_mgmt_interface['gwaddr'] }} 200 +{% endblock default_route %} +! +{% block source_loopback %} +! Set ip source to loopback for bgp learned routes +route-map RM_SET_SRC permit 10 + set src {{ minigraph_lo_interfaces[0]['addr'] }} +! +{% set lo_ipv6_addrs = [] %} +{% if minigraph_lo_interfaces is defined %} +{% for interface in minigraph_lo_interfaces %} +{% if interface['addr'] is defined and interface['addr']|ipv6 %} +{% if lo_ipv6_addrs.append(interface['addr']) %} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% if lo_ipv6_addrs|length > 0 %} +route-map RM_SET_SRC6 permit 10 + set src {{ lo_ipv6_addrs[0] }} +! +{% endif %} +ip protocol bgp route-map RM_SET_SRC +! +{% if lo_ipv6_addrs|length > 0 %} +ipv6 protocol bgp route-map RM_SET_SRC6 +! +{% endif %} +{% endblock source_loopback %} +! +{% block logging %} +log syslog informational +log facility local4 +{% endblock logging %} +! +