github.com/osrg/gobgp/v3@v3.30.0/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 17 import collections 18 import json 19 from itertools import chain 20 from threading import Thread 21 import subprocess 22 import os 23 24 import netaddr 25 import toml 26 import yaml 27 28 from lib.base import ( 29 community_str, 30 wait_for_completion, 31 BGPContainer, 32 CmdBuffer, 33 BGP_ATTR_TYPE_AS_PATH, 34 BGP_ATTR_TYPE_NEXT_HOP, 35 BGP_ATTR_TYPE_MULTI_EXIT_DISC, 36 BGP_ATTR_TYPE_LOCAL_PREF, 37 BGP_ATTR_TYPE_COMMUNITIES, 38 BGP_ATTR_TYPE_MP_REACH_NLRI, 39 GRACEFUL_RESTART_TIME, 40 LONG_LIVED_GRACEFUL_RESTART_TIME, 41 BGP_FSM_IDLE, 42 BGP_FSM_ACTIVE, 43 BGP_FSM_ESTABLISHED, 44 yellow, 45 indent, 46 local, 47 ) 48 49 50 def extract_path_attribute(path, typ): 51 for a in path['attrs']: 52 if a['type'] == typ: 53 return a 54 return None 55 56 57 class GoBGPContainer(BGPContainer): 58 59 SHARED_VOLUME = '/root/shared_volume' 60 QUAGGA_VOLUME = '/etc/quagga' 61 62 def __init__(self, name, asn, router_id, ctn_image_name='osrg/gobgp', 63 log_level='debug', zebra=False, config_format='toml', 64 zapi_version=2, bgp_config=None, ospfd_config=None, 65 zebra_multipath_enabled=False): 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.zebra_multipath_enabled = zebra_multipath_enabled 81 self.config_format = config_format 82 83 # bgp_config is equivalent to config.BgpConfigSet structure 84 # Example: 85 # bgp_config = { 86 # 'global': { 87 # 'confederation': { 88 # 'config': { 89 # 'identifier': 10, 90 # 'member-as-list': [65001], 91 # } 92 # }, 93 # }, 94 # } 95 self.bgp_config = bgp_config or {} 96 97 # To start OSPFd in GoBGP container, specify 'ospfd_config' as a dict 98 # type value. 99 # Example: 100 # ospfd_config = { 101 # 'redistributes': [ 102 # 'connected', 103 # ], 104 # 'networks': { 105 # '192.168.1.0/24': '0.0.0.0', # <network>: <area> 106 # }, 107 # } 108 self.ospfd_config = ospfd_config or {} 109 110 def _start_gobgp(self, graceful_restart=False): 111 c = CmdBuffer() 112 c << '#!/bin/sh' 113 c << '/go/bin/gobgpd -f {0}/gobgpd.conf -l {1} -p {2} -t {3} > ' \ 114 '{0}/gobgpd.log 2>&1'.format(self.SHARED_VOLUME, self.log_level, '-r' if graceful_restart else '', self.config_format) 115 cmd = 'echo "{0:s}" > {1}/start.sh'.format(str(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 -KILL 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 list(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', add_path_enabled=False): 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 if add_path_enabled: 286 return self._get_rib(json.loads(output)) 287 288 ret = [p[0] for p in json.loads(output).values()] 289 for p in ret: 290 p["nexthop"] = self._get_nexthop(p) 291 p["aspath"] = self._get_as_path(p) 292 p["prefix"] = p['nlri']['prefix'] 293 p["local-pref"] = self._get_local_pref(p) 294 p["med"] = self._get_med(p) 295 return ret 296 297 def get_adj_rib_in(self, peer, prefix='', rf='ipv4', add_path_enabled=False): 298 return self._get_adj_rib('in', peer, prefix, rf, add_path_enabled) 299 300 def get_adj_rib_out(self, peer, prefix='', rf='ipv4', add_path_enabled=False): 301 return self._get_adj_rib('out', peer, prefix, rf, add_path_enabled) 302 303 def get_neighbor(self, peer): 304 cmd = 'gobgp -j neighbor {0}'.format(self.peer_name(peer)) 305 return json.loads(self.local(cmd, capture=True)) 306 307 def get_neighbor_state(self, peer): 308 s = self.get_neighbor(peer)['state']['session_state'] 309 if s == 1: 310 return BGP_FSM_IDLE 311 elif s == 3: 312 return BGP_FSM_ACTIVE 313 elif s == 6: 314 return BGP_FSM_ESTABLISHED 315 return "unknown" 316 317 def clear_policy(self): 318 self.policies = {} 319 for info in self.peers.values(): 320 info['policies'] = {} 321 self.prefix_set = [] 322 self.neighbor_set = [] 323 self.statements = [] 324 325 def set_prefix_set(self, ps): 326 if not isinstance(ps, list): 327 ps = [ps] 328 self.prefix_set = ps 329 330 def add_prefix_set(self, ps): 331 if self.prefix_set is None: 332 self.prefix_set = [] 333 self.prefix_set.append(ps) 334 335 def set_neighbor_set(self, ns): 336 if not isinstance(ns, list): 337 ns = [ns] 338 self.neighbor_set = ns 339 340 def add_neighbor_set(self, ns): 341 if self.neighbor_set is None: 342 self.neighbor_set = [] 343 self.neighbor_set.append(ns) 344 345 def set_bgp_defined_set(self, bs): 346 self.bgp_set = bs 347 348 def create_config(self): 349 self._create_config_bgp() 350 if self.zebra: 351 local('mkdir -p {0}'.format(self.quagga_config_dir)) 352 local('chmod 777 {0}'.format(self.quagga_config_dir)) 353 self._create_config_zebra() 354 if self.ospfd_config: 355 self._create_config_ospfd() 356 357 def _merge_dict(self, dct, merge_dct): 358 for k, v in merge_dct.items(): 359 if (k in dct and isinstance(dct[k], dict) 360 and isinstance(merge_dct[k], collections.abc.Mapping)): 361 self._merge_dict(dct[k], merge_dct[k]) 362 else: 363 dct[k] = merge_dct[k] 364 365 def _create_config_bgp(self): 366 config = { 367 'global': { 368 'config': { 369 'as': self.asn, 370 'router-id': self.router_id, 371 }, 372 'route-selection-options': { 373 'config': { 374 'external-compare-router-id': True, 375 }, 376 }, 377 }, 378 'neighbors': [], 379 } 380 381 self._merge_dict(config, self.bgp_config) 382 383 if self.zebra and self.zapi_version == 2: 384 config['global']['use-multiple-paths'] = {'config': {'enabled': True}} 385 else: 386 config['global']['use-multiple-paths'] = {'config': {'enabled': self.zebra_multipath_enabled}} 387 388 for peer, info in self.peers.items(): 389 afi_safi_list = [] 390 if info['interface'] != '': 391 afi_safi_list.append({'config': {'afi-safi-name': 'ipv4-unicast'}}) 392 afi_safi_list.append({'config': {'afi-safi-name': 'ipv6-unicast'}}) 393 else: 394 version = netaddr.IPNetwork(info['neigh_addr']).version 395 if version == 4: 396 afi_safi_list.append({'config': {'afi-safi-name': 'ipv4-unicast'}}) 397 elif version == 6: 398 afi_safi_list.append({'config': {'afi-safi-name': 'ipv6-unicast'}}) 399 else: 400 Exception('invalid ip address version. {0}'.format(version)) 401 402 if info['vpn']: 403 afi_safi_list.append({'config': {'afi-safi-name': 'l3vpn-ipv4-unicast'}}) 404 afi_safi_list.append({'config': {'afi-safi-name': 'l3vpn-ipv6-unicast'}}) 405 afi_safi_list.append({'config': {'afi-safi-name': 'l2vpn-evpn'}}) 406 afi_safi_list.append({'config': {'afi-safi-name': 'rtc'}, 'route-target-membership': {'config': {'deferral-time': 10}}}) 407 408 if info['flowspec']: 409 afi_safi_list.append({'config': {'afi-safi-name': 'ipv4-flowspec'}}) 410 afi_safi_list.append({'config': {'afi-safi-name': 'l3vpn-ipv4-flowspec'}}) 411 afi_safi_list.append({'config': {'afi-safi-name': 'ipv6-flowspec'}}) 412 afi_safi_list.append({'config': {'afi-safi-name': 'l3vpn-ipv6-flowspec'}}) 413 414 if info['mup']: 415 afi_safi_list.append({'config': {'afi-safi-name': 'ipv4-mup'}}) 416 afi_safi_list.append({'config': {'afi-safi-name': 'ipv6-mup'}}) 417 418 neigh_addr = None 419 interface = None 420 peer_as = None 421 if info['interface'] == '': 422 neigh_addr = info['neigh_addr'].split('/')[0] 423 peer_as = info['remote_as'] 424 else: 425 interface = info['interface'] 426 n = { 427 'config': { 428 'neighbor-address': neigh_addr, 429 'neighbor-interface': interface, 430 'peer-as': peer_as, 431 'auth-password': info['passwd'], 432 'vrf': info['vrf'], 433 'remove-private-as': info['remove_private_as'], 434 }, 435 'afi-safis': afi_safi_list, 436 'timers': { 437 'config': { 438 'connect-retry': 10, 439 }, 440 }, 441 'transport': { 442 'config': {}, 443 }, 444 } 445 446 n['as-path-options'] = {'config': {}} 447 if info['allow_as_in'] > 0: 448 n['as-path-options']['config']['allow-own-as'] = info['allow_as_in'] 449 if info['replace_peer_as']: 450 n['as-path-options']['config']['replace-peer-as'] = info['replace_peer_as'] 451 452 if ':' in info['local_addr']: 453 n['transport']['config']['local-address'] = info['local_addr'].split('/')[0] 454 455 if info['passive']: 456 n['transport']['config']['passive-mode'] = True 457 458 if info['is_rs_client']: 459 n['route-server'] = {'config': {'route-server-client': True}} 460 461 if info['local_as']: 462 n['config']['local-as'] = info['local_as'] 463 464 if info['prefix_limit']: 465 for v in afi_safi_list: 466 v['prefix-limit'] = {'config': {'max-prefixes': info['prefix_limit'], 'shutdown-threshold-pct': 80}} 467 468 if info['graceful_restart'] is not None: 469 n['graceful-restart'] = {'config': {'enabled': True, 'restart-time': GRACEFUL_RESTART_TIME}} 470 for afi_safi in afi_safi_list: 471 afi_safi['mp-graceful-restart'] = {'config': {'enabled': True}} 472 473 if info['llgr'] is not None: 474 n['graceful-restart']['config']['restart-time'] = 1 475 n['graceful-restart']['config']['long-lived-enabled'] = True 476 for afi_safi in afi_safi_list: 477 afi_safi['long-lived-graceful-restart'] = {'config': {'enabled': True, 'restart-time': LONG_LIVED_GRACEFUL_RESTART_TIME}} 478 479 if info['is_rr_client']: 480 cluster_id = self.router_id 481 if 'cluster_id' in info and info['cluster_id'] is not None: 482 cluster_id = info['cluster_id'] 483 n['route-reflector'] = {'config': {'route-reflector-client': True, 484 'route-reflector-cluster-id': cluster_id}} 485 486 if info["addpath"] > 0: 487 n["add-paths"] = { 488 "config": {"receive": True, "send-max": info["addpath"]} 489 } 490 491 if len(info.get('default-policy', [])) + len(info.get('policies', [])) > 0: 492 n['apply-policy'] = {'config': {}} 493 494 for typ, p in info.get('policies', {}).items(): 495 n['apply-policy']['config']['{0}-policy-list'.format(typ)] = [p['name']] 496 497 def _f(v): 498 if v == 'reject': 499 return 'reject-route' 500 elif v == 'accept': 501 return 'accept-route' 502 raise Exception('invalid default policy type {0}'.format(v)) 503 504 for typ, d in info.get('default-policy', {}).items(): 505 n['apply-policy']['config']['default-{0}-policy'.format(typ)] = _f(d) 506 507 if info['treat_as_withdraw']: 508 n['error-handling'] = {'config': {'treat-as-withdraw': True}} 509 510 config['neighbors'].append(n) 511 512 config['defined-sets'] = {} 513 if self.prefix_set: 514 config['defined-sets']['prefix-sets'] = self.prefix_set 515 516 if self.neighbor_set: 517 config['defined-sets']['neighbor-sets'] = self.neighbor_set 518 519 if self.bgp_set: 520 config['defined-sets']['bgp-defined-sets'] = self.bgp_set 521 522 policy_list = [] 523 for p in self.policies.values(): 524 policy = {'name': p['name']} 525 if 'statements' in p: 526 policy['statements'] = p['statements'] 527 policy_list.append(policy) 528 529 if len(policy_list) > 0: 530 config['policy-definitions'] = policy_list 531 532 if self.zebra: 533 config['zebra'] = {'config': {'enabled': True, 534 'redistribute-route-type-list': ['connect'], 535 'version': self.zapi_version}} 536 537 with open('{0}/gobgpd.conf'.format(self.config_dir), 'w') as f: 538 print(yellow('[{0}\'s new gobgpd.conf]'.format(self.name))) 539 if self.config_format == "toml": 540 raw = toml.dumps(config) 541 elif self.config_format == "yaml": 542 raw = yaml.dump(config) 543 elif self.config_format == "json": 544 raw = json.dumps(config) 545 else: 546 raise Exception('invalid config_format {0}'.format(self.config_format)) 547 raw = raw.strip() 548 print(yellow(indent(raw))) 549 f.write(raw) 550 551 def _create_config_zebra(self): 552 c = CmdBuffer() 553 c << 'hostname zebra' 554 c << 'password zebra' 555 c << 'log file {0}/zebra.log'.format(self.QUAGGA_VOLUME) 556 c << 'debug zebra packet' 557 c << 'debug zebra kernel' 558 c << 'debug zebra rib' 559 c << 'ipv6 forwarding' 560 c << '' 561 562 with open('{0}/zebra.conf'.format(self.quagga_config_dir), 'w') as f: 563 print(yellow('[{0}\'s new zebra.conf]'.format(self.name))) 564 c = str(c).strip() 565 print(yellow(indent(c))) 566 f.writelines(c) 567 568 def _create_config_ospfd(self): 569 c = CmdBuffer() 570 c << 'hostname ospfd' 571 c << 'password zebra' 572 c << 'router ospf' 573 for redistribute in self.ospfd_config.get('redistributes', []): 574 c << ' redistribute {0}'.format(redistribute) 575 for network, area in list(self.ospfd_config.get('networks', {}).items()): 576 c << ' network {0} area {1}'.format(network, area) 577 c << 'log file {0}/ospfd.log'.format(self.QUAGGA_VOLUME) 578 c << '' 579 580 with open('{0}/ospfd.conf'.format(self.quagga_config_dir), 'w') as f: 581 print(yellow('[{0}\'s new ospfd.conf]'.format(self.name))) 582 print(yellow(indent(str(c)))) 583 f.writelines(str(c)) 584 585 def reload_config(self): 586 for daemon in self._get_enabled_quagga_daemons(): 587 self.local('pkill -SIGHUP {0}'.format(daemon), capture=True) 588 self.local('pkill -SIGHUP gobgpd', capture=True) 589 self._wait_for_boot() 590 591 def add_route(self, route, rf='ipv4', attribute=None, aspath=None, 592 community=None, med=None, extendedcommunity=None, 593 nexthop=None, matchs=None, thens=None, 594 local_pref=None, identifier=None, reload_config=False): 595 if not self._is_running(): 596 raise RuntimeError('GoBGP is not yet running') 597 598 self.routes.setdefault(route, []) 599 path = { 600 'prefix': route, 601 'rf': rf, 602 'attr': attribute, 603 'next-hop': nexthop, 604 'as-path': aspath, 605 'community': community, 606 'med': med, 607 'local-pref': local_pref, 608 'extended-community': extendedcommunity, 609 'identifier': identifier, 610 'matchs': matchs, 611 'thens': thens, 612 } 613 614 c = CmdBuffer(' ') 615 c << 'gobgp global rib -a {0} add'.format(rf) 616 if rf in ('ipv4', 'ipv6'): 617 c << route 618 if path['identifier']: 619 c << 'identifier {0}'.format(path['identifier']) 620 if path['next-hop']: 621 c << 'nexthop {0}'.format(path['next-hop']) 622 if path['local-pref']: 623 c << 'local-pref {0}'.format(path['local-pref']) 624 if path['med']: 625 c << 'med {0}'.format(path['med']) 626 if path['community']: 627 comm = str(path['community']) 628 if isinstance(path['community'], (list, tuple)): 629 comm = ','.join(path['community']) 630 c << 'community {0}'.format(comm) 631 elif rf.endswith('-flowspec'): 632 c << 'match {0}'.format(' '.join(path['matchs'])) 633 c << 'then {0}'.format(' '.join(path['thens'])) 634 else: 635 raise Exception('unsupported address family: {0}'.format(rf)) 636 self.local(str(c), capture=True) 637 638 self.routes[route].append(path) 639 640 def del_route(self, route, identifier=None, reload_config=True): 641 if not self._is_running(): 642 raise RuntimeError('GoBGP is not yet running') 643 644 if route not in self.routes: 645 return 646 647 new_paths = [] 648 for path in self.routes[route]: 649 if path['identifier'] != identifier: 650 new_paths.append(path) 651 else: 652 r = CmdBuffer(' ') 653 r << 'gobgp global -a {0}'.format(path['rf']) 654 prefix = path['prefix'] 655 if path['rf'].endswith('-flowspec'): 656 prefix = 'match {0}'.format(' '.join(path['matchs'])) 657 r << 'rib del {0}'.format(prefix) 658 if identifier: 659 r << 'identifier {0}'.format(identifier) 660 cmd = str(r) 661 self.local(cmd, capture=True) 662 self.routes[route] = new_paths 663 # no need to reload config 664 665 666 class RawGoBGPContainer(GoBGPContainer): 667 def __init__(self, name, config, ctn_image_name='osrg/gobgp', 668 log_level='debug', zebra=False, config_format='yaml'): 669 if config_format == "toml": 670 d = toml.loads(config) 671 elif config_format == "yaml": 672 d = yaml.load(config) 673 elif config_format == "json": 674 d = json.loads(config) 675 else: 676 raise Exception('invalid config format {0}'.format(config_format)) 677 asn = d['global']['config']['as'] 678 router_id = d['global']['config']['router-id'] 679 self.config = config 680 super(RawGoBGPContainer, self).__init__(name, asn, router_id, 681 ctn_image_name, log_level, 682 zebra, config_format) 683 684 def create_config(self): 685 with open('{0}/gobgpd.conf'.format(self.config_dir), 'w') as f: 686 print(yellow('[{0}\'s new gobgpd.conf]'.format(self.name))) 687 print(yellow(indent(self.config))) 688 f.write(self.config)