def create_config(self): # Currently, supports only single peer c = CmdBuffer('\n') c << '[DEFAULT]' c << 'log_dir = {0}'.format(self.SHARED_VOLUME) c << 'use_stderr = False' c << '[message]' c << 'write_disk = True' c << 'write_dir = {0}/data/bgp/'.format(self.SHARED_VOLUME) c << 'format = json' if self.peers: info = next(iter(list(self.peers.values()))) remote_as = info['remote_as'] neigh_addr = info['neigh_addr'].split('/')[0] local_as = info['local_as'] or self.asn local_addr = info['local_addr'].split('/')[0] c << '[bgp]' c << 'afi_safi = ipv4, ipv6, vpnv4, vpnv6, flowspec, evpn' c << 'remote_as = {0}'.format(remote_as) c << 'remote_addr = {0}'.format(neigh_addr) c << 'local_as = {0}'.format(local_as) c << 'local_addr = {0}'.format(local_addr) with open('{0}/yabgp.ini'.format(self.config_dir), 'w') as f: print(yellow('[{0}\'s new yabgp.ini]'.format(self.name))) print(yellow(indent(str(c)))) f.writelines(str(c))
def reload_config(self): daemon = ['gobgpd'] if self.zebra: daemon.append('zebra') if self.ospfd_config: daemon.append('ospfd') for d in daemon: cmd = '/usr/bin/pkill {0} -SIGHUP'.format(d) self.local(cmd) for v in chain.from_iterable(self.routes.itervalues()): if v['rf'] == 'ipv4' or v['rf'] == 'ipv6': r = CmdBuffer(' ') r << 'gobgp global -a {0}'.format(v['rf']) r << 'rib add {0}'.format(v['prefix']) if v['identifier']: r << 'identifier {0}'.format(v['identifier']) if v['next-hop']: r << 'nexthop {0}'.format(v['next-hop']) if v['local-pref']: r << 'local-pref {0}'.format(v['local-pref']) if v['med']: r << 'med {0}'.format(v['med']) if v['community']: r << 'community {0}'.format(','.join( v['community']) if isinstance(v['community'], ( list, tuple)) else v['community']) cmd = str(r) elif v['rf'] == 'ipv4-flowspec' or v['rf'] == 'ipv6-flowspec': cmd = 'gobgp global '\ 'rib add match {0} then {1} -a {2}'.format(' '.join(v['matchs']), ' '.join(v['thens']), v['rf']) else: raise Exception('unsupported route faily: {0}'.format(v['rf'])) self.local(cmd)
def reload_config(self): for daemon in self._get_enabled_quagga_daemons(): self.local('pkill {0} -SIGHUP'.format(daemon), capture=True) self.local('pkill gobgpd -SIGHUP', capture=True) self._wait_for_boot() for v in chain.from_iterable(self.routes.itervalues()): if v['rf'] == 'ipv4' or v['rf'] == 'ipv6': r = CmdBuffer(' ') r << 'gobgp global -a {0}'.format(v['rf']) r << 'rib add {0}'.format(v['prefix']) if v['identifier']: r << 'identifier {0}'.format(v['identifier']) if v['next-hop']: r << 'nexthop {0}'.format(v['next-hop']) if v['local-pref']: r << 'local-pref {0}'.format(v['local-pref']) if v['med']: r << 'med {0}'.format(v['med']) if v['community']: r << 'community {0}'.format( ','.join(v['community']) if isinstance(v['community'], (list, tuple)) else v['community']) cmd = str(r) elif v['rf'] == 'ipv4-flowspec' or v['rf'] == 'ipv6-flowspec': cmd = 'gobgp global '\ 'rib add match {0} then {1} -a {2}'.format(' '.join(v['matchs']), ' '.join(v['thens']), v['rf']) else: raise Exception('unsupported route family: {0}'.format(v['rf'])) self.local(cmd)
def del_route(self, route, identifier=None, reload_config=False): if not self._is_running(): raise RuntimeError('Quagga/Zebra is not yet running') path = None new_paths = [] for p in self.routes.get(route, []): if p['identifier'] != identifier: new_paths.append(p) else: path = p if not path: return rf = path['rf'] c = CmdBuffer(' ') c << "vtysh -c 'configure terminal'" c << "-c 'router bgp {0}'".format(self.asn) c << "-c 'address-family {0} unicast'".format(rf) c << "-c 'no network {0}'".format(route) self.local(str(c), capture=True) # Delete route-map after deleting prefix self._vtysh_del_route_map(path) self.routes[route] = new_paths
def create_config(self): # Manpage of exabgp.conf(5): # https://github.com/Exa-Networks/exabgp/blob/master/doc/man/exabgp.conf.5 cmd = CmdBuffer('\n') for peer, info in self.peers.iteritems(): cmd << 'neighbor {0} {{'.format(info['neigh_addr'].split('/')[0]) cmd << ' router-id {0};'.format(self.router_id) cmd << ' local-address {0};'.format( info['local_addr'].split('/')[0]) cmd << ' local-as {0};'.format(self.asn) cmd << ' peer-as {0};'.format(peer.asn) caps = [] if info['as2']: caps.append(' asn4 disable;') if info['addpath']: caps.append(' add-path send/receive;') if caps: cmd << ' capability {' for cap in caps: cmd << cap cmd << ' }' if info['passwd']: cmd << ' md5-password "{0}";'.format(info['passwd']) if info['passive']: cmd << ' passive;' cmd << '}' with open('{0}/exabgpd.conf'.format(self.config_dir), 'w') as f: print colors.yellow('[{0}\'s new exabgpd.conf]'.format(self.name)) print colors.yellow(str(cmd)) f.write(str(cmd))
def _construct_ip_unicast(self, path): cmd = CmdBuffer(' ') cmd << str(path['prefix']) if path['next-hop']: cmd << 'next-hop {0}'.format(path['next-hop']) else: cmd << 'next-hop self' return str(cmd)
def _curl_send_update(self, path, peer): c = CmdBuffer(' ') c << "curl -X POST" c << "-u admin:admin" c << "-H 'Content-Type: application/json'" c << "http://localhost:8801/v1/peer/{0}/send/update".format(peer) c << "-d '{0}'".format(json.dumps(path)) return json.loads(self.local(str(c), capture=True))
def reload_config(self): cmd = CmdBuffer(' ') cmd << 'docker exec' cmd << '{0} cp {1}/bgp.conf'.format(self.name, self.SHARED_VOLUME) cmd << '/etc/bagpipe-bgp/' local(str(cmd), capture=True) cmd = 'docker exec {0} service bagpipe-bgp restart'.format(self.name) local(cmd, capture=True)
def _curl_is_running(self): c = CmdBuffer(' ') c << "curl -X GET" c << "-u admin:admin" c << "-H 'Content-Type: application/json'" c << "http://localhost:8801/v1/" c << "> /dev/null 2>&1; echo $?" return self.local(str(c), capture=True) == '0'
def _start_exabgp(self): cmd = CmdBuffer(' ') cmd << 'env exabgp.log.destination={0}/exabgpd.log'.format( self.SHARED_VOLUME) cmd << "exabgp.tcp.bind='0.0.0.0' exabgp.tcp.port=179" cmd << './exabgp/sbin/exabgp {0}/exabgpd.conf'.format( self.SHARED_VOLUME) self.local(str(cmd), detach=True)
def add_route(self, route, rf='ipv4', attribute=None, aspath=None, community=None, med=None, extendedcommunity=None, nexthop=None, matchs=None, thens=None, local_pref=None, identifier=None, reload_config=False): if not self._is_running(): raise RuntimeError('GoBGP is not yet running') self.routes.setdefault(route, []) path = { 'prefix': route, 'rf': rf, 'attr': attribute, 'next-hop': nexthop, 'as-path': aspath, 'community': community, 'med': med, 'local-pref': local_pref, 'extended-community': extendedcommunity, 'identifier': identifier, 'matchs': matchs, 'thens': thens, } c = CmdBuffer(' ') c << 'gobgp global rib -a {0} add'.format(rf) if rf in ('ipv4', 'ipv6'): c << route if path['identifier']: c << 'identifier {0}'.format(path['identifier']) if path['next-hop']: c << 'nexthop {0}'.format(path['next-hop']) if path['local-pref']: c << 'local-pref {0}'.format(path['local-pref']) if path['med']: c << 'med {0}'.format(path['med']) if path['community']: comm = str(path['community']) if isinstance(path['community'], (list, tuple)): comm = ','.join(path['community']) c << 'community {0}'.format(comm) elif rf.endswith('-flowspec'): c << 'match {0}'.format(' '.join(path['matchs'])) c << 'then {0}'.format(' '.join(path['thens'])) else: raise Exception('unsupported address family: {0}'.format(rf)) self.local(str(c), capture=True) self.routes[route].append(path)
def _vtysh_del_route_map(self, path): route_map_name = path.get('route_map', '') if not route_map_name: return c = CmdBuffer(' ') c << "vtysh -c 'configure terminal'" c << "-c 'no route-map {0}'".format(route_map_name) self.local(str(c), capture=True)
def run(self): super(BagpipeContainer, self).run() cmd = CmdBuffer(' ') cmd << 'docker exec' cmd << '{0} cp {1}/bgp.conf'.format(self.name, self.SHARED_VOLUME) cmd << '/etc/bagpipe-bgp/' local(str(cmd), capture=True) cmd = 'docker exec {0} service bagpipe-bgp start'.format(self.name) local(cmd, capture=True)
def _get_adj_rib(self, peer, in_out='in'): peer_addr = self.peer_name(peer) c = CmdBuffer(' ') c << "curl -X GET" c << "-u admin:admin" c << "-H 'Content-Type: application/json'" c << "http://localhost:8801/v1-ext/peer/{0}/adj-rib-{1}".format( peer_addr, in_out) return json.loads(self.local(str(c), capture=True))
def _start_bird(self): c = CmdBuffer() c << '#!/bin/sh' c << 'bird' cmd = 'echo "{0:s}" > {1}/start.sh'.format(c, self.config_dir) local(cmd) cmd = 'chmod 755 {0}/start.sh'.format(self.config_dir) local(cmd) self.local('{0}/start.sh'.format(self.SHARED_VOLUME))
def _start_exabgp(self): cmd = CmdBuffer(' ') cmd << 'env exabgp.log.destination={0}/exabgpd.log'.format( self.SHARED_VOLUME) cmd << 'exabgp.daemon.user=root' cmd << 'exabgp.daemon.pid={0}'.format(self.PID_FILE) cmd << 'exabgp.tcp.bind="0.0.0.0" exabgp.tcp.port=179' cmd << 'exabgp {0}/exabgpd.conf'.format(self.SHARED_VOLUME) self.local(str(cmd), detach=True)
def _start_gobgp(self, graceful_restart=False): c = CmdBuffer() c << '#!/bin/sh' c << '/go/bin/gobgpd -f {0}/gobgpd.conf -l {1} -p {2} -t {3} > ' \ '{0}/gobgpd.log 2>&1'.format(self.SHARED_VOLUME, self.log_level, '-r' if graceful_restart else '', self.config_format) cmd = 'echo "{0:s}" > {1}/start.sh'.format(str(c), self.config_dir) local(cmd, capture=True) cmd = "chmod 755 {0}/start.sh".format(self.config_dir) local(cmd, capture=True) self.local("{0}/start.sh".format(self.SHARED_VOLUME), detach=True)
def _construct_flowspec(self, path): cmd = CmdBuffer(' ') cmd << '{ match {' for match in path['matchs']: cmd << '{0};'.format(match) cmd << '} then {' for then in path['thens']: cmd << '{0};'.format(then) cmd << '} }' return str(cmd)
def add_route(self, route, rf='ipv4', attribute=None, aspath=None, community=None, med=None, extendedcommunity=None, nexthop=None, matchs=None, thens=None, local_pref=None, identifier=None, reload_config=False): if not self._is_running(): raise RuntimeError('Quagga/Zebra is not yet running') if rf not in ('ipv4', 'ipv6'): raise ValueError('Unsupported address family: {0}'.format(rf)) self.routes.setdefault(route, []) path = { 'prefix': route, 'rf': rf, 'next-hop': nexthop, 'as-path': aspath, 'community': community, 'med': med, 'local-pref': local_pref, 'extended-community': extendedcommunity, # Note: The following settings are not yet supported on this # implementation. 'attr': None, 'identifier': None, 'matchs': None, 'thens': None, } # Prepare route-map before adding prefix route_map_name = self._vtysh_add_route_map(path) path['route_map'] = route_map_name c = CmdBuffer(' ') c << "vtysh -c 'configure terminal'" c << "-c 'router bgp {0}'".format(self.asn) if rf == 'ipv6': c << "-c 'address-family ipv6'" if route_map_name: c << "-c 'network {0} route-map {1}'".format(route, route_map_name) else: c << "-c 'network {0}'".format(route) self.local(str(c), capture=True) self.routes[route].append(path)
def _create_config_zebra(self): c = CmdBuffer() c << 'hostname zebra' c << 'password zebra' c << 'log file {0}/zebra.log'.format(self.QUAGGA_VOLUME) c << 'debug zebra packet' c << 'debug zebra kernel' c << 'debug zebra rib' c << '' with open('{0}/zebra.conf'.format(self.quagga_config_dir), 'w') as f: print colors.yellow('[{0}\'s new zebra.conf]'.format(self.name)) print colors.yellow(indent(str(c))) f.writelines(str(c))
def create_config(self): c = CmdBuffer() c << 'router id {0};'.format(self.router_id) for peer, info in self.peers.items(): c << 'protocol bgp {' c << ' local as {0};'.format(self.asn) n_addr = info['neigh_addr'].split('/')[0] c << ' neighbor {0} as {1};'.format(n_addr, peer.asn) c << ' multihop;' c << '}' with open('{0}/bird.conf'.format(self.config_dir), 'w') as f: print(yellow('[{0}\'s new bird.conf]'.format(self.name))) print(yellow(indent(str(c)))) f.writelines(str(c))
def _create_config_ospfd(self): c = CmdBuffer() c << 'hostname ospfd' c << 'password zebra' c << 'router ospf' for redistribute in self.ospfd_config.get('redistributes', []): c << ' redistribute {0}'.format(redistribute) for network, area in list(self.ospfd_config.get('networks', {}).items()): c << ' network {0} area {1}'.format(network, area) c << 'log file {0}/ospfd.log'.format(self.QUAGGA_VOLUME) c << '' with open('{0}/ospfd.conf'.format(self.quagga_config_dir), 'w') as f: print(yellow('[{0}\'s new ospfd.conf]'.format(self.name))) print(yellow(indent(str(c)))) f.writelines(str(c))
def _create_config_zebra(self): c = CmdBuffer() c << 'hostname zebra' c << 'password zebra' c << 'log file {0}/zebra.log'.format(self.SHARED_VOLUME) c << 'debug zebra packet' c << 'debug zebra kernel' c << 'debug zebra rib' c << 'ipv6 forwarding' c << '' with open('{0}/zebra.conf'.format(self.config_dir), 'w') as f: print(yellow('[{0}\'s new zebra.conf]'.format(self.name))) c = str(c).strip() print(yellow(indent(c))) f.writelines(c)
def del_route(self, route, identifier=None, reload_config=True): if route not in self.routes: return new_paths = [] for path in self.routes[route]: if path['identifier'] != identifier: new_paths.append(path) else: r = CmdBuffer(' ') r << 'gobgp global -a {0}'.format(path['rf']) r << 'rib del {0}'.format(path['prefix']) if identifier: r << 'identifier {0}'.format(identifier) cmd = str(r) self.local(cmd) self.routes[route] = new_paths
def _construct_path_attributes(self, path): cmd = CmdBuffer(' ') if path['as-path']: cmd << 'as-path [{0}]'.format(' '.join( str(i) for i in path['as-path'])) if path['med']: cmd << 'med {0}'.format(path['med']) if path['local-pref']: cmd << 'local-preference {0}'.format(path['local-pref']) if path['community']: cmd << 'community [{0}]'.format(' '.join( c for c in path['community'])) if path['extended-community']: cmd << 'extended-community [{0}]'.format( path['extended-community']) if path['attr']: cmd << 'attribute [ {0} ]'.format(path['attr']) return str(cmd)
def _create_config_zebra(self): c = CmdBuffer() c << 'hostname zebra' c << 'password zebra' for name, settings in self.zebra_config.get('interfaces', {}).items(): c << 'interface {0}'.format(name) for setting in settings: c << str(setting) for route in self.zebra_config.get('routes', []): c << str(route) c << 'log file {0}/zebra.log'.format(self.SHARED_VOLUME) c << 'debug zebra packet' c << 'debug zebra kernel' c << 'debug zebra rib' c << '' with open('{0}/zebra.conf'.format(self.config_dir), 'w') as f: print colors.yellow('[{0}\'s new zebra.conf]'.format(self.name)) print colors.yellow(indent(str(c))) f.writelines(str(c))
def _construct_path(self, path, rf='ipv4', is_withdraw=False): cmd = CmdBuffer(' ') if rf in ['ipv4', 'ipv6']: cmd << 'route' cmd << self._construct_ip_unicast(path) elif rf in ['ipv4-flowspec', 'ipv6-flowspec']: cmd << 'flow route' cmd << self._construct_flowspec(path) else: raise ValueError('unsupported address family: %s' % rf) if path['identifier']: cmd << 'path-information {0}'.format(path['identifier']) if not is_withdraw: # Withdrawal should not require path attributes cmd << self._construct_path_attributes(path) return str(cmd)
def _update_exabgp(self): if self.exabgp_path == '': return c = CmdBuffer() c << '#!/bin/bash' remotepath = '/root/exabgp' localpath = self.exabgp_path local('cp -r {0} {1}'.format(localpath, self.config_dir)) c << 'cp {0}/etc/exabgp/exabgp.env {1}'.format(remotepath, self.SHARED_VOLUME) c << 'sed -i -e \'s/all = false/all = true/g\' {0}/exabgp.env'.format(self.SHARED_VOLUME) c << 'cp -r {0}/exabgp {1}'.format(self.SHARED_VOLUME, remotepath[:-1 * len('exabgp')]) c << 'cp {0}/exabgp.env {1}/etc/exabgp/'.format(self.SHARED_VOLUME, remotepath) cmd = 'echo "{0:s}" > {1}/update.sh'.format(c, self.config_dir) local(cmd, capture=True) cmd = 'chmod 755 {0}/update.sh'.format(self.config_dir) local(cmd, capture=True) cmd = '{0}/update.sh'.format(self.SHARED_VOLUME) self.local(cmd)
def create_config(self): c = CmdBuffer() c << '[BGP]' if len(self.ip_addrs) > 0: c << 'local_address={0}'.format(self.ip_addrs[0][1].split('/')[0]) for info in list(self.peers.values()): c << 'peers={0}'.format(info['neigh_addr'].split('/')[0]) c << 'my_as={0}'.format(self.asn) c << 'enable_rtc=True' c << '[API]' c << 'api_host=localhost' c << 'api_port=8082' c << '[DATAPLANE_DRIVER_IPVPN]' c << 'dataplane_driver = DummyDataplaneDriver' c << '[DATAPLANE_DRIVER_EVPN]' c << 'dataplane_driver = DummyDataplaneDriver' with open('{0}/bgp.conf'.format(self.config_dir), 'w') as f: print(yellow(str(c))) f.writelines(str(c))
def _vtysh_add_route_map(self, path): supported_attributes = ( 'next-hop', 'as-path', 'community', 'med', 'local-pref', 'extended-community', ) if not any([path[k] for k in supported_attributes]): return '' c = CmdBuffer(' ') route_map_name = 'RM-{0}'.format(path['prefix']) c << "vtysh -c 'configure terminal'" c << "-c 'route-map {0} permit 10'".format(route_map_name) if path['next-hop']: if path['rf'] == 'ipv4': c << "-c 'set ip next-hop {0}'".format(path['next-hop']) elif path['rf'] == 'ipv6': c << "-c 'set ipv6 next-hop {0}'".format(path['next-hop']) else: raise ValueError('Unsupported address family: {0}'.format( path['rf'])) if path['as-path']: as_path = ' '.join([str(n) for n in path['as-path']]) c << "-c 'set as-path prepend {0}'".format(as_path) if path['community']: comm = ' '.join(path['community']) c << "-c 'set community {0}'".format(comm) if path['med']: c << "-c 'set metric {0}'".format(path['med']) if path['local-pref']: c << "-c 'set local-preference {0}'".format(path['local-pref']) if path['extended-community']: # Note: Currently only RT is supported. extcomm = ' '.join(path['extended-community']) c << "-c 'set extcommunity rt {0}'".format(extcomm) self.local(str(c), capture=True) return route_map_name