github.com/osrg/gobgp@v2.0.0+incompatible/test/lib/gobgp.py (about) 1 # Copyright (C) 2015 Nippon Telegraph and Telephone Corporation. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 # implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 16 from __future__ import absolute_import 17 18 import collections 19 import json 20 from itertools import chain 21 from threading import Thread 22 import subprocess 23 import os 24 25 from fabric import colors 26 from fabric.api import local 27 from fabric.utils import indent 28 import netaddr 29 import toml 30 import yaml 31 32 from lib.base import ( 33 community_str, 34 wait_for_completion, 35 BGPContainer, 36 CmdBuffer, 37 BGP_ATTR_TYPE_AS_PATH, 38 BGP_ATTR_TYPE_NEXT_HOP, 39 BGP_ATTR_TYPE_MULTI_EXIT_DISC, 40 BGP_ATTR_TYPE_LOCAL_PREF, 41 BGP_ATTR_TYPE_COMMUNITIES, 42 BGP_ATTR_TYPE_MP_REACH_NLRI, 43 GRACEFUL_RESTART_TIME, 44 LONG_LIVED_GRACEFUL_RESTART_TIME, 45 BGP_FSM_IDLE, 46 BGP_FSM_ACTIVE, 47 BGP_FSM_ESTABLISHED, 48 ) 49 50 51 def extract_path_attribute(path, typ): 52 for a in path['attrs']: 53 if a['type'] == typ: 54 return a 55 return None 56 57 58 class GoBGPContainer(BGPContainer): 59 60 SHARED_VOLUME = '/root/shared_volume' 61 QUAGGA_VOLUME = '/etc/quagga' 62 63 def __init__(self, name, asn, router_id, ctn_image_name='osrg/gobgp', 64 log_level='debug', zebra=False, config_format='toml', 65 zapi_version=2, bgp_config=None, ospfd_config=None): 66 super(GoBGPContainer, self).__init__(name, asn, router_id, 67 ctn_image_name) 68 self.shared_volumes.append((self.config_dir, self.SHARED_VOLUME)) 69 self.quagga_config_dir = '{0}/quagga'.format(self.config_dir) 70 self.shared_volumes.append((self.quagga_config_dir, self.QUAGGA_VOLUME)) 71 72 self.log_level = log_level 73 self.prefix_set = None 74 self.neighbor_set = None 75 self.bgp_set = None 76 self.statements = None 77 self.default_policy = None 78 self.zebra = zebra 79 self.zapi_version = zapi_version 80 self.config_format = config_format 81 82 # bgp_config is equivalent to config.BgpConfigSet structure 83 # Example: 84 # bgp_config = { 85 # 'global': { 86 # 'confederation': { 87 # 'config': { 88 # 'identifier': 10, 89 # 'member-as-list': [65001], 90 # } 91 # }, 92 # }, 93 # } 94 self.bgp_config = bgp_config or {} 95 96 # To start OSPFd in GoBGP container, specify 'ospfd_config' as a dict 97 # type value. 98 # Example: 99 # ospfd_config = { 100 # 'redistributes': [ 101 # 'connected', 102 # ], 103 # 'networks': { 104 # '192.168.1.0/24': '0.0.0.0', # <network>: <area> 105 # }, 106 # } 107 self.ospfd_config = ospfd_config or {} 108 109 def _start_gobgp(self, graceful_restart=False): 110 c = CmdBuffer() 111 c << '#!/bin/sh' 112 c << '/go/bin/gobgpd -f {0}/gobgpd.conf -l {1} -p {2} -t {3} > ' \ 113 '{0}/gobgpd.log 2>&1'.format(self.SHARED_VOLUME, self.log_level, '-r' if graceful_restart else '', self.config_format) 114 115 cmd = 'echo "{0:s}" > {1}/start.sh'.format(c, self.config_dir) 116 local(cmd, capture=True) 117 cmd = "chmod 755 {0}/start.sh".format(self.config_dir) 118 local(cmd, capture=True) 119 self.local("{0}/start.sh".format(self.SHARED_VOLUME), detach=True) 120 121 def start_gobgp(self, graceful_restart=False): 122 if self._is_running(): 123 raise RuntimeError('GoBGP is already running') 124 self._start_gobgp(graceful_restart=graceful_restart) 125 self._wait_for_boot() 126 127 def stop_gobgp(self): 128 self.local("pkill -INT gobgpd") 129 130 def _start_zebra(self): 131 if self.zapi_version == 2: 132 daemon_bin = '/usr/lib/quagga/zebra' 133 else: 134 daemon_bin = 'zebra' 135 cmd = '{0} -f {1}/zebra.conf'.format(daemon_bin, self.QUAGGA_VOLUME) 136 self.local(cmd, detach=True) 137 138 def _start_ospfd(self): 139 if self.zapi_version == 2: 140 daemon_bin = '/usr/lib/quagga/ospfd' 141 else: 142 daemon_bin = 'ospfd' 143 cmd = '{0} -f {1}/ospfd.conf'.format(daemon_bin, self.QUAGGA_VOLUME) 144 self.local(cmd, detach=True) 145 146 def _get_enabled_quagga_daemons(self): 147 daemons = [] 148 if self.zebra: 149 daemons.append('zebra') 150 if self.ospfd_config: 151 daemons.append('ospfd') 152 return daemons 153 154 def _is_running(self): 155 return self.local('gobgp global' 156 ' > /dev/null 2>&1; echo $?', capture=True) == '0' 157 158 def _wait_for_boot(self): 159 for daemon in self._get_enabled_quagga_daemons(): 160 def _f_quagga(): 161 ret = self.local("vtysh -d {0} -c 'show run' > /dev/null 2>&1; echo $?".format(daemon), capture=True) 162 return ret == '0' 163 164 wait_for_completion(_f_quagga) 165 166 wait_for_completion(self._is_running) 167 168 def run(self): 169 super(GoBGPContainer, self).run() 170 if self.zebra: 171 self._start_zebra() 172 if self.ospfd_config: 173 self._start_ospfd() 174 self._start_gobgp() 175 self._wait_for_boot() 176 return self.WAIT_FOR_BOOT 177 178 @staticmethod 179 def _get_as_path(path): 180 asps = (p['as_paths'] for p in path['attrs'] 181 if p['type'] == BGP_ATTR_TYPE_AS_PATH and 'as_paths' in p and p['as_paths'] is not None) 182 asps = chain.from_iterable(asps) 183 asns = (asp['asns'] for asp in asps) 184 return list(chain.from_iterable(asns)) 185 186 @staticmethod 187 def _get_nexthop(path): 188 for p in path['attrs']: 189 if p['type'] == BGP_ATTR_TYPE_NEXT_HOP or p['type'] == BGP_ATTR_TYPE_MP_REACH_NLRI: 190 return p['nexthop'] 191 192 @staticmethod 193 def _get_local_pref(path): 194 for p in path['attrs']: 195 if p['type'] == BGP_ATTR_TYPE_LOCAL_PREF: 196 return p['value'] 197 return None 198 199 @staticmethod 200 def _get_med(path): 201 for p in path['attrs']: 202 if p['type'] == BGP_ATTR_TYPE_MULTI_EXIT_DISC: 203 return p['metric'] 204 return None 205 206 @staticmethod 207 def _get_community(path): 208 for p in path['attrs']: 209 if p['type'] == BGP_ATTR_TYPE_COMMUNITIES: 210 return [community_str(c) for c in p['communities']] 211 return None 212 213 def _get_rib(self, dests_dict): 214 dests = [] 215 for k, v in dests_dict.items(): 216 for p in v: 217 p["nexthop"] = self._get_nexthop(p) 218 p["aspath"] = self._get_as_path(p) 219 p["local-pref"] = self._get_local_pref(p) 220 p["community"] = self._get_community(p) 221 p["med"] = self._get_med(p) 222 p["prefix"] = k 223 path_id = p.get("id", None) 224 if path_id: 225 p["identifier"] = p["id"] 226 dests.append({'paths': v, 'prefix': k}) 227 return dests 228 229 def _trigger_peer_cmd(self, cmd, peer): 230 peer_addr = self.peer_name(peer) 231 cmd = 'gobgp neighbor {0} {1}'.format(peer_addr, cmd) 232 self.local(cmd) 233 234 def disable_peer(self, peer): 235 self._trigger_peer_cmd('disable', peer) 236 237 def enable_peer(self, peer): 238 self._trigger_peer_cmd('enable', peer) 239 240 def reset(self, peer): 241 self._trigger_peer_cmd('reset', peer) 242 243 def softreset(self, peer, rf='ipv4', type='in'): 244 self._trigger_peer_cmd('softreset{0} -a {1}'.format(type, rf), peer) 245 246 def get_local_rib(self, peer, prefix='', rf='ipv4'): 247 peer_addr = self.peer_name(peer) 248 cmd = 'gobgp -j neighbor {0} local {1} -a {2}'.format(peer_addr, prefix, rf) 249 output = self.local(cmd, capture=True) 250 return self._get_rib(json.loads(output)) 251 252 def get_global_rib(self, prefix='', rf='ipv4'): 253 cmd = 'gobgp -j global rib {0} -a {1}'.format(prefix, rf) 254 output = self.local(cmd, capture=True) 255 return self._get_rib(json.loads(output)) 256 257 def monitor_global_rib(self, queue, rf='ipv4'): 258 host = self.ip_addrs[0][1].split('/')[0] 259 260 if not os.path.exists('{0}/gobgp'.format(self.config_dir)): 261 self.local('cp /go/bin/gobgp {0}/'.format(self.SHARED_VOLUME)) 262 263 args = '{0}/gobgp -u {1} -j monitor global rib -a {2}'.format(self.config_dir, host, rf).split(' ') 264 265 def monitor(): 266 process = subprocess.Popen(args, stdout=subprocess.PIPE) 267 for line in iter(process.stdout.readline, ''): 268 p = json.loads(line)[0] 269 p["nexthop"] = self._get_nexthop(p) 270 p["aspath"] = self._get_as_path(p) 271 p["local-pref"] = self._get_local_pref(p) 272 p["med"] = self._get_med(p) 273 queue.put(p) 274 275 t = Thread(target=monitor) 276 t.daemon = True 277 t.start() 278 279 def _get_adj_rib(self, adj_type, peer, prefix='', rf='ipv4'): 280 peer_addr = self.peer_name(peer) 281 cmd = 'gobgp neighbor {0} adj-{1} {2} -a {3} -j'.format(peer_addr, 282 adj_type, 283 prefix, rf) 284 output = self.local(cmd, capture=True) 285 ret = [p[0] for p in json.loads(output).itervalues()] 286 for p in ret: 287 p["nexthop"] = self._get_nexthop(p) 288 p["aspath"] = self._get_as_path(p) 289 p["prefix"] = p['nlri']['prefix'] 290 p["local-pref"] = self._get_local_pref(p) 291 p["med"] = self._get_med(p) 292 return ret 293 294 def get_adj_rib_in(self, peer, prefix='', rf='ipv4'): 295 return self._get_adj_rib('in', peer, prefix, rf) 296 297 def get_adj_rib_out(self, peer, prefix='', rf='ipv4'): 298 return self._get_adj_rib('out', peer, prefix, rf) 299 300 def get_neighbor(self, peer): 301 cmd = 'gobgp -j neighbor {0}'.format(self.peer_name(peer)) 302 return json.loads(self.local(cmd, capture=True)) 303 304 def get_neighbor_state(self, peer): 305 s = self.get_neighbor(peer)['state']['session_state'] 306 if s == 1: 307 return BGP_FSM_IDLE 308 elif s == 3: 309 return BGP_FSM_ACTIVE 310 elif s == 6: 311 return BGP_FSM_ESTABLISHED 312 return "unknown" 313 314 def clear_policy(self): 315 self.policies = {} 316 for info in self.peers.itervalues(): 317 info['policies'] = {} 318 self.prefix_set = [] 319 self.neighbor_set = [] 320 self.statements = [] 321 322 def set_prefix_set(self, ps): 323 if not isinstance(ps, list): 324 ps = [ps] 325 self.prefix_set = ps 326 327 def add_prefix_set(self, ps): 328 if self.prefix_set is None: 329 self.prefix_set = [] 330 self.prefix_set.append(ps) 331 332 def set_neighbor_set(self, ns): 333 if not isinstance(ns, list): 334 ns = [ns] 335 self.neighbor_set = ns 336 337 def add_neighbor_set(self, ns): 338 if self.neighbor_set is None: 339 self.neighbor_set = [] 340 self.neighbor_set.append(ns) 341 342 def set_bgp_defined_set(self, bs): 343 self.bgp_set = bs 344 345 def create_config(self): 346 self._create_config_bgp() 347 if self.zebra: 348 local('mkdir -p {0}'.format(self.quagga_config_dir)) 349 local('chmod 777 {0}'.format(self.quagga_config_dir)) 350 self._create_config_zebra() 351 if self.ospfd_config: 352 self._create_config_ospfd() 353 354 def _merge_dict(self, dct, merge_dct): 355 for k, v in merge_dct.iteritems(): 356 if (k in dct and isinstance(dct[k], dict) 357 and isinstance(merge_dct[k], collections.Mapping)): 358 self._merge_dict(dct[k], merge_dct[k]) 359 else: 360 dct[k] = merge_dct[k] 361 362 def _create_config_bgp(self): 363 config = { 364 'global': { 365 'config': { 366 'as': self.asn, 367 'router-id': self.router_id, 368 }, 369 'route-selection-options': { 370 'config': { 371 'external-compare-router-id': True, 372 }, 373 }, 374 }, 375 'neighbors': [], 376 } 377 378 self._merge_dict(config, self.bgp_config) 379 380 if self.zebra and self.zapi_version == 2: 381 config['global']['use-multiple-paths'] = {'config': {'enabled': True}} 382 383 for peer, info in self.peers.iteritems(): 384 afi_safi_list = [] 385 if info['interface'] != '': 386 afi_safi_list.append({'config': {'afi-safi-name': 'ipv4-unicast'}}) 387 afi_safi_list.append({'config': {'afi-safi-name': 'ipv6-unicast'}}) 388 else: 389 version = netaddr.IPNetwork(info['neigh_addr']).version 390 if version == 4: 391 afi_safi_list.append({'config': {'afi-safi-name': 'ipv4-unicast'}}) 392 elif version == 6: 393 afi_safi_list.append({'config': {'afi-safi-name': 'ipv6-unicast'}}) 394 else: 395 Exception('invalid ip address version. {0}'.format(version)) 396 397 if info['vpn']: 398 afi_safi_list.append({'config': {'afi-safi-name': 'l3vpn-ipv4-unicast'}}) 399 afi_safi_list.append({'config': {'afi-safi-name': 'l3vpn-ipv6-unicast'}}) 400 afi_safi_list.append({'config': {'afi-safi-name': 'l2vpn-evpn'}}) 401 afi_safi_list.append({'config': {'afi-safi-name': 'rtc'}, 'route-target-membership': {'config': {'deferral-time': 10}}}) 402 403 if info['flowspec']: 404 afi_safi_list.append({'config': {'afi-safi-name': 'ipv4-flowspec'}}) 405 afi_safi_list.append({'config': {'afi-safi-name': 'l3vpn-ipv4-flowspec'}}) 406 afi_safi_list.append({'config': {'afi-safi-name': 'ipv6-flowspec'}}) 407 afi_safi_list.append({'config': {'afi-safi-name': 'l3vpn-ipv6-flowspec'}}) 408 409 neigh_addr = None 410 interface = None 411 peer_as = None 412 if info['interface'] == '': 413 neigh_addr = info['neigh_addr'].split('/')[0] 414 peer_as = info['remote_as'] 415 else: 416 interface = info['interface'] 417 n = { 418 'config': { 419 'neighbor-address': neigh_addr, 420 'neighbor-interface': interface, 421 'peer-as': peer_as, 422 'auth-password': info['passwd'], 423 'vrf': info['vrf'], 424 'remove-private-as': info['remove_private_as'], 425 }, 426 'afi-safis': afi_safi_list, 427 'timers': { 428 'config': { 429 'connect-retry': 10, 430 }, 431 }, 432 'transport': { 433 'config': {}, 434 }, 435 } 436 437 n['as-path-options'] = {'config': {}} 438 if info['allow_as_in'] > 0: 439 n['as-path-options']['config']['allow-own-as'] = info['allow_as_in'] 440 if info['replace_peer_as']: 441 n['as-path-options']['config']['replace-peer-as'] = info['replace_peer_as'] 442 443 if ':' in info['local_addr']: 444 n['transport']['config']['local-address'] = info['local_addr'].split('/')[0] 445 446 if info['passive']: 447 n['transport']['config']['passive-mode'] = True 448 449 if info['is_rs_client']: 450 n['route-server'] = {'config': {'route-server-client': True}} 451 452 if info['local_as']: 453 n['config']['local-as'] = info['local_as'] 454 455 if info['prefix_limit']: 456 for v in afi_safi_list: 457 v['prefix-limit'] = {'config': {'max-prefixes': info['prefix_limit'], 'shutdown-threshold-pct': 80}} 458 459 if info['graceful_restart'] is not None: 460 n['graceful-restart'] = {'config': {'enabled': True, 'restart-time': GRACEFUL_RESTART_TIME}} 461 for afi_safi in afi_safi_list: 462 afi_safi['mp-graceful-restart'] = {'config': {'enabled': True}} 463 464 if info['llgr'] is not None: 465 n['graceful-restart']['config']['restart-time'] = 1 466 n['graceful-restart']['config']['long-lived-enabled'] = True 467 for afi_safi in afi_safi_list: 468 afi_safi['long-lived-graceful-restart'] = {'config': {'enabled': True, 'restart-time': LONG_LIVED_GRACEFUL_RESTART_TIME}} 469 470 if info['is_rr_client']: 471 cluster_id = self.router_id 472 if 'cluster_id' in info and info['cluster_id'] is not None: 473 cluster_id = info['cluster_id'] 474 n['route-reflector'] = {'config': {'route-reflector-client': True, 475 'route-reflector-cluster-id': cluster_id}} 476 477 if info['addpath']: 478 n['add-paths'] = {'config': {'receive': True, 479 'send-max': 16}} 480 481 if len(info.get('default-policy', [])) + len(info.get('policies', [])) > 0: 482 n['apply-policy'] = {'config': {}} 483 484 for typ, p in info.get('policies', {}).iteritems(): 485 n['apply-policy']['config']['{0}-policy-list'.format(typ)] = [p['name']] 486 487 def _f(v): 488 if v == 'reject': 489 return 'reject-route' 490 elif v == 'accept': 491 return 'accept-route' 492 raise Exception('invalid default policy type {0}'.format(v)) 493 494 for typ, d in info.get('default-policy', {}).iteritems(): 495 n['apply-policy']['config']['default-{0}-policy'.format(typ)] = _f(d) 496 497 if info['treat_as_withdraw']: 498 n['error-handling'] = {'config': {'treat-as-withdraw': True}} 499 500 config['neighbors'].append(n) 501 502 config['defined-sets'] = {} 503 if self.prefix_set: 504 config['defined-sets']['prefix-sets'] = self.prefix_set 505 506 if self.neighbor_set: 507 config['defined-sets']['neighbor-sets'] = self.neighbor_set 508 509 if self.bgp_set: 510 config['defined-sets']['bgp-defined-sets'] = self.bgp_set 511 512 policy_list = [] 513 for p in self.policies.itervalues(): 514 policy = {'name': p['name']} 515 if 'statements' in p: 516 policy['statements'] = p['statements'] 517 policy_list.append(policy) 518 519 if len(policy_list) > 0: 520 config['policy-definitions'] = policy_list 521 522 if self.zebra: 523 config['zebra'] = {'config': {'enabled': True, 524 'redistribute-route-type-list': ['connect'], 525 'version': self.zapi_version}} 526 527 with open('{0}/gobgpd.conf'.format(self.config_dir), 'w') as f: 528 print colors.yellow('[{0}\'s new gobgpd.conf]'.format(self.name)) 529 if self.config_format is 'toml': 530 raw = toml.dumps(config) 531 elif self.config_format is 'yaml': 532 raw = yaml.dump(config) 533 elif self.config_format is 'json': 534 raw = json.dumps(config) 535 else: 536 raise Exception('invalid config_format {0}'.format(self.config_format)) 537 print colors.yellow(indent(raw)) 538 f.write(raw) 539 540 def _create_config_zebra(self): 541 c = CmdBuffer() 542 c << 'hostname zebra' 543 c << 'password zebra' 544 c << 'log file {0}/zebra.log'.format(self.QUAGGA_VOLUME) 545 c << 'debug zebra packet' 546 c << 'debug zebra kernel' 547 c << 'debug zebra rib' 548 c << 'ipv6 forwarding' 549 c << '' 550 551 with open('{0}/zebra.conf'.format(self.quagga_config_dir), 'w') as f: 552 print colors.yellow('[{0}\'s new zebra.conf]'.format(self.name)) 553 print colors.yellow(indent(str(c))) 554 f.writelines(str(c)) 555 556 def _create_config_ospfd(self): 557 c = CmdBuffer() 558 c << 'hostname ospfd' 559 c << 'password zebra' 560 c << 'router ospf' 561 for redistribute in self.ospfd_config.get('redistributes', []): 562 c << ' redistribute {0}'.format(redistribute) 563 for network, area in self.ospfd_config.get('networks', {}).items(): 564 c << ' network {0} area {1}'.format(network, area) 565 c << 'log file {0}/ospfd.log'.format(self.QUAGGA_VOLUME) 566 c << '' 567 568 with open('{0}/ospfd.conf'.format(self.quagga_config_dir), 'w') as f: 569 print colors.yellow('[{0}\'s new ospfd.conf]'.format(self.name)) 570 print colors.yellow(indent(str(c))) 571 f.writelines(str(c)) 572 573 def reload_config(self): 574 for daemon in self._get_enabled_quagga_daemons(): 575 self.local('pkill -SIGHUP {0}'.format(daemon), capture=True) 576 self.local('pkill -SIGHUP gobgpd', capture=True) 577 self._wait_for_boot() 578 579 def add_route(self, route, rf='ipv4', attribute=None, aspath=None, 580 community=None, med=None, extendedcommunity=None, 581 nexthop=None, matchs=None, thens=None, 582 local_pref=None, identifier=None, reload_config=False): 583 if not self._is_running(): 584 raise RuntimeError('GoBGP is not yet running') 585 586 self.routes.setdefault(route, []) 587 path = { 588 'prefix': route, 589 'rf': rf, 590 'attr': attribute, 591 'next-hop': nexthop, 592 'as-path': aspath, 593 'community': community, 594 'med': med, 595 'local-pref': local_pref, 596 'extended-community': extendedcommunity, 597 'identifier': identifier, 598 'matchs': matchs, 599 'thens': thens, 600 } 601 602 c = CmdBuffer(' ') 603 c << 'gobgp global rib -a {0} add'.format(rf) 604 if rf in ('ipv4', 'ipv6'): 605 c << route 606 if path['identifier']: 607 c << 'identifier {0}'.format(path['identifier']) 608 if path['next-hop']: 609 c << 'nexthop {0}'.format(path['next-hop']) 610 if path['local-pref']: 611 c << 'local-pref {0}'.format(path['local-pref']) 612 if path['med']: 613 c << 'med {0}'.format(path['med']) 614 if path['community']: 615 comm = str(path['community']) 616 if isinstance(path['community'], (list, tuple)): 617 comm = ','.join(path['community']) 618 c << 'community {0}'.format(comm) 619 elif rf.endswith('-flowspec'): 620 c << 'match {0}'.format(' '.join(path['matchs'])) 621 c << 'then {0}'.format(' '.join(path['thens'])) 622 else: 623 raise Exception('unsupported address family: {0}'.format(rf)) 624 self.local(str(c), capture=True) 625 626 self.routes[route].append(path) 627 628 def del_route(self, route, identifier=None, reload_config=True): 629 if not self._is_running(): 630 raise RuntimeError('GoBGP is not yet running') 631 632 if route not in self.routes: 633 return 634 635 new_paths = [] 636 for path in self.routes[route]: 637 if path['identifier'] != identifier: 638 new_paths.append(path) 639 else: 640 r = CmdBuffer(' ') 641 r << 'gobgp global -a {0}'.format(path['rf']) 642 prefix = path['prefix'] 643 if path['rf'].endswith('-flowspec'): 644 prefix = 'match {0}'.format(' '.join(path['matchs'])) 645 r << 'rib del {0}'.format(prefix) 646 if identifier: 647 r << 'identifier {0}'.format(identifier) 648 cmd = str(r) 649 self.local(cmd, capture=True) 650 self.routes[route] = new_paths 651 # no need to reload config 652 653 654 class RawGoBGPContainer(GoBGPContainer): 655 def __init__(self, name, config, ctn_image_name='osrg/gobgp', 656 log_level='debug', zebra=False, config_format='yaml'): 657 if config_format is 'toml': 658 d = toml.loads(config) 659 elif config_format is 'yaml': 660 d = yaml.load(config) 661 elif config_format is 'json': 662 d = json.loads(config) 663 else: 664 raise Exception('invalid config format {0}'.format(config_format)) 665 asn = d['global']['config']['as'] 666 router_id = d['global']['config']['router-id'] 667 self.config = config 668 super(RawGoBGPContainer, self).__init__(name, asn, router_id, 669 ctn_image_name, log_level, 670 zebra, config_format) 671 672 def create_config(self): 673 with open('{0}/gobgpd.conf'.format(self.config_dir), 'w') as f: 674 print colors.yellow('[{0}\'s new gobgpd.conf]'.format(self.name)) 675 print colors.yellow(indent(self.config)) 676 f.write(self.config)