Skip to content

Commit 165c8c7

Browse files
authored
Merge pull request #5 from CiscoDevNet/bgp-rib
Bgp rib
2 parents 1134dd7 + 632bff1 commit 165c8c7

10 files changed

Lines changed: 1267 additions & 26 deletions

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ Go here for [details](https://github.com/CiscoDevNet/pathman-sr/tree/master/dClo
180180
## To use with Docker
181181
Go here for [details](https://github.com/CiscoDevNet/pathman-sr/tree/master/docker)
182182

183+
## For Tools to help you get PCEP, BGP-LS and Netconf setup
184+
Go here for [details](https://github.com/CiscoDevNet/pathman-sr/tree/master/tools)
185+
183186

184187

185188

netconf.py

Lines changed: 554 additions & 0 deletions
Large diffs are not rendered by default.

odl_test.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#! /usr/bin/env python2.7
2+
"""
3+
* Copyright (c) 2016 by Cisco Systems, Inc.
4+
* All rights reserved.
5+
6+
odl_test.py
7+
- get a quick check on PCEP, BGP and Netconf from your ODL controller
8+
9+
Niklas Montin, 20161212, niklas@cisco.com
10+
11+
"""
12+
import argparse
13+
import sys
14+
import json
15+
import logging
16+
from pathman_sr import LOGGING
17+
from netconf import pcep_test, bgp_test, netconf_test, controller
18+
19+
version = '1.0'
20+
21+
22+
def nprint(mydict):
23+
print json.dumps(mydict, sort_keys=True,indent=4, separators=(',', ': '))
24+
25+
26+
def print_pcep(name=None):
27+
reply = pcep_test(name)
28+
nprint(reply)
29+
30+
31+
def print_bgp(name=None, address=None):
32+
reply = bgp_test(name, address)
33+
nprint(reply)
34+
35+
36+
def print_netconf(name=None):
37+
reply = netconf_test(name)
38+
nprint(reply)
39+
40+
41+
if __name__ == '__main__':
42+
LOGGING['root']['handlers'] = ['console','logtofile']
43+
logging.config.dictConfig(LOGGING)
44+
logging.info("This is initializing the automation log")
45+
46+
logging.captureWarnings(True)
47+
48+
p = argparse.ArgumentParser(
49+
prog=sys.argv[0],
50+
description='Test BGP, PCEP and Netconf output ODL Controller',
51+
version=version,
52+
epilog='Copyright (c) 2017 by Cisco Systems, Inc. All Rights Reserved'
53+
)
54+
55+
subp = p.add_subparsers(help='commands', dest='command')
56+
57+
pcep_p = subp.add_parser("pcep", help='print pcep output')
58+
pcep_p.add_argument('--address', type=str, help='address of node to list')
59+
60+
bgp_p = subp.add_parser("bgp", help='print bgp output')
61+
bgp_p.add_argument('--name', type=str, help='name of node to list')
62+
bgp_p.add_argument('--address', type=str, help='address of node to list')
63+
64+
netconf_p = subp.add_parser("netconf", help='list netconf nodes')
65+
netconf_p.add_argument('--name', type=str, help='name of node to list')
66+
67+
p.add_argument('--controller_ip', default=controller['odl_ip'], type=str, help='ODL Controller ip address')
68+
p.add_argument('--controller_port', default=controller['odl_port'], type=str, help='ODL port')
69+
p.add_argument('--user', default=controller['any_user'], type=str, help='ODL user')
70+
p.add_argument('--password', default=controller['any_pass'], type=str, help='ODL password')
71+
72+
ns = p.parse_args()
73+
logging.info("Parser: %s" % ns)
74+
75+
if ns.controller_ip:
76+
controller['odl_ip'] = ns.controller_ip
77+
if ns.controller_port:
78+
controller['odl_port'] = ns.controller_port
79+
if ns.user:
80+
controller['any_user'] = ns.user
81+
# odl_user = ns.user
82+
if ns.password:
83+
controller['any_pass'] = ns.password
84+
85+
if ns.command == 'pcep':
86+
print_pcep(ns.address)
87+
88+
elif ns.command == 'bgp':
89+
print_bgp(ns.name, ns.address)
90+
91+
elif ns.command == 'netconf':
92+
print_netconf(ns.name)
93+
94+
# Bye bye

pathman_ini.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,15 @@
1616
log_level - controls the the logging depth, chnage to 'DEBUG' for more detail
1717
odl_user - username for odl controller restconf access
1818
odl_password - password for odl controller restconf access
19+
20+
Updated:
21+
- 20161204, Niklas, Added MyBGP to get SIDs from bgp-rib
1922
2023
2124
"""
25+
26+
sid_saves = 'static_sids.json'
27+
2228
#odl_ip = '127.0.0.1'
2329
odl_ip = '198.18.1.80'
2430
odl_port = '8181'
@@ -28,3 +34,123 @@
2834
log_level = 'INFO'
2935
odl_user = 'admin'
3036
odl_password = 'admin'
37+
38+
import requests, json, logging
39+
40+
class MyBGP(object):
41+
42+
def __init__(self, method='http', controller_ip=odl_ip, controller_port=odl_port, user=odl_user, passw=odl_password):
43+
'''note to self: add dict for item type
44+
self.dict['flavor']['list']
45+
self.dict['images']['index']
46+
'''
47+
temp_dict = {'list':[], 'index':[]}
48+
self.token = ''
49+
self.ip = controller_ip
50+
self.user = user
51+
self.password = passw
52+
self.dicts = { 'nodes':{'list':[], 'index':[]},
53+
'prefixes':{'list':[], 'index':[]},
54+
'links':{'list':[], 'index':[]},
55+
56+
}
57+
url = '{}://{}:{}/restconf/operational/bgp-rib:bgp-rib/rib/example-bgp-rib/loc-rib/tables/bgp-linkstate:linkstate-address-family/bgp-linkstate:linkstate-subsequent-address-family/linkstate-routes'
58+
self.url = url.format(method, controller_ip, controller_port)
59+
self.creds = {'username':user,'password':passw}
60+
self.status = 0
61+
self.error = ""
62+
63+
result = self._get_url(self.url, headers={'content-type': 'application/json'})
64+
if result:
65+
linkstate = result.json()
66+
for link in linkstate[u'bgp-linkstate:linkstate-routes']['linkstate-route']:
67+
if 'node-descriptors' in link.keys():
68+
self.dicts['nodes']['list'].append(link)
69+
elif 'prefix-descriptors' in link.keys():
70+
if 'sr-prefix' in link['attributes']['prefix-attributes'].keys():
71+
self.dicts['prefixes']['list'].append(link)
72+
elif 'link-descriptors' in link.keys():
73+
self.dicts['links']['list'].append(link)
74+
else:
75+
logging.error('unexpected item: {}'.format(link))
76+
77+
self.dicts['nodes']['index'] = [node['attributes'].get('node-attributes', {}).get('ipv4-router-id', '0.0.0.0') for node in self.dicts['nodes']['list']]
78+
self.dicts['prefixes']['index'] = [prefix['prefix-descriptors']['ip-reachability-information'] for prefix in self.dicts['prefixes']['list']]
79+
self.dicts['links']['index'] = [link['link-descriptors']['ipv4-interface-address'] for link in self.dicts['links']['list']]
80+
81+
else:
82+
logging.info('Nothing retrieved')
83+
84+
def _get_url(self, url, headers):
85+
'''common get function'''
86+
try:
87+
response = requests.get(url, headers = headers, auth=(self.user,self.password), verify=False)
88+
logging.info("status code: %s"% response.status_code)
89+
if response.status_code in [200, 202, 204]:
90+
return response
91+
else:
92+
logging.error("Error code: %s, %s" %(response.status_code, response.text))
93+
94+
except requests.exceptions.ConnectionError, e:
95+
logging.error('Connection Error to %s: %s' % (self.ip, e.message))
96+
97+
return
98+
99+
def _delete_url(self, url, headers):
100+
try:
101+
response = requests.delete(url, headers = headers)
102+
logging.info("status code: %s"% response.status_code)
103+
if response.status_code in [200, 202, 204]:
104+
return response
105+
else:
106+
logging.error("Error code: %s, %s" %(response.status_code, response.text))
107+
except requests.exceptions.ConnectionError, e:
108+
logging.error('Connection Error to %s: %s' % (self.ip, e.message))
109+
return
110+
111+
def _post_url(self, url, data, headers):
112+
try:
113+
response = requests.post(url, data=data, headers = headers)
114+
return response
115+
except requests.exceptions.ConnectionError, e:
116+
logging.error('Connection Error to %s: %s' % (self.ip, e.message))
117+
return
118+
119+
def item_details(self, item, name):
120+
'''get details for a service'''
121+
try:
122+
index = self.dicts[item]['index'].index(name)
123+
return self.dicts[item]['list'][index]
124+
except ValueError:
125+
return
126+
127+
def get_sr_info_old(self):
128+
sr_info = {}
129+
for node in self.dicts['nodes']['list']:
130+
base = node['attributes']['node-attributes']['sr-capabilities']['local-label']
131+
loopback = node['attributes']['node-attributes']['ipv4-router-id']
132+
prefix = self.item_details('prefixes', loopback + '/32')
133+
label = prefix['attributes']['prefix-attributes']['sr-prefix']['local-label']
134+
sr_info.update({loopback: int(base) + int(label)})
135+
return sr_info
136+
137+
def get_sr_info(self):
138+
sr_info = {}
139+
for node in self.dicts['nodes']['list']:
140+
base = node['attributes'].get('node-attributes', {}).get('sr-capabilities', {}).get('local-label',)
141+
if base:
142+
loopback = node['attributes'].get('node-attributes', {}).get('ipv4-router-id', )
143+
if loopback:
144+
prefix = self.item_details('prefixes', loopback + '/32')
145+
146+
if any(x in prefix['attributes']['prefix-attributes']['sr-prefix'].keys() for x in ['local-label', 'sid']):
147+
if 'local-label' in prefix['attributes']['prefix-attributes']['sr-prefix'].keys():
148+
label = prefix['attributes']['prefix-attributes']['sr-prefix']['local-label']
149+
else:
150+
label = prefix['attributes']['prefix-attributes']['sr-prefix']['sid']
151+
sr_info.update({loopback: int(base) + int(label)})
152+
else:
153+
logging.error('no sid info')
154+
return sr_info
155+
156+
# Bye bye

pathman_sr.py

Lines changed: 67 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@
4747
20160912, Niklas - ver 5.9c - Fixed Boron version check from false positives
4848
20160919, Niklas - ver 5.9d - getTopo reply format changed
4949
20160924, Niklas - ver 5.9e - Updated metrics selection
50-
20161226, Niklas - ver 5.9f - Updates from branches; multi-area/level and sid fix
50+
20161204, Niklas - Ver 5.9f - Added BGP-RIB support to retrieve SIDs. Requires XRv 6.1.x, or higher.
51+
20161210, Niklas - ver 5.9g - Added Netconf-modules for users to add their nodes to netconf
52+
- Added static netconf mappings for users ho give up on netconf
53+
20161226, Niklas - ver 5.9h - Multi area/level fix for bgp-ls and sid bug.
54+
20170202, Niklas - ver 5.9i - Refactored sid_list to sid_saves to avoid duplicate use
5155
"""
5256
__author__ = 'niklas'
5357

@@ -64,7 +68,9 @@
6468

6569

6670
#==============================================================
67-
version = '5.9f'
71+
72+
version = '5.9i'
73+
6874
# Defaults overridden by pathman_ini.py
6975
odl_ip = '127.0.0.1'
7076
odl_port = '8181'
@@ -301,6 +307,24 @@ def formatTime(self, record, datefmt=None):
301307
}
302308

303309

310+
def dict_to_file(mdict, file):
311+
with open(file,'w') as f:
312+
f.write(json.dumps(mdict))
313+
logging.info("writing %s" % mdict)
314+
315+
316+
def file_to_dict(file):
317+
mdict = {}
318+
if os.path.exists(file):
319+
with open(file,'r') as f:
320+
string = f.read()
321+
user_files = json.loads(string)
322+
logging.info("reading %s" % user_files)
323+
for item in user_files:
324+
mdict.update({item:user_files[item]})
325+
return mdict
326+
327+
304328
def version_check():
305329
"""modified from odl_gateway"""
306330
url = get_version
@@ -368,18 +392,19 @@ def get_config(url_list, match):
368392
try:
369393
sid = get_config([get_node_isis_config, get_node_ospf_config], 'prefix-sid')
370394
rid = get_config([get_node_bgp_config], 'router-id')
371-
logging.info('rid: %s, sid: %s' %(rid, sid))
395+
logging.info('rid: %s, sid: %s' % (rid, sid))
372396

373397
if sid != -1:
374-
node_configs.update({node:sid})
398+
node_configs.update({node: sid})
375399
logging.info("got netconf data for: %s" % node)
376400
if rid != -1:
377-
rid_dict.update({rid:sid})
401+
rid_dict.update({rid: sid})
378402
logging.info("router-id: %s" % rid)
379403
else:
380-
logging.error("No rid for: %s" % node)
404+
logging.info("No sid for: %s" % node)
381405
else:
382-
logging.info("No sid for: %s" % node)
406+
logging.error("No rid for: %s" % node)
407+
383408
except:
384409
logging.error("failure to get netconf data for: %s" % node)
385410
return node_configs, rid_dict
@@ -408,25 +433,41 @@ def keyd_dict_walker(mdict, key):
408433
def node_sr_update(node_list):
409434
def update(node, temp_sid):
410435
if 'value' in temp_sid.keys():
411-
node_list[node_list.index(node)] = node._replace(sid = temp_sid['value'])
412-
logging.info("SR sid updated for: %s" % node.name)
436+
node_list[node_list.index(node)] = node._replace(sid=temp_sid['value'])
437+
logging.info("SR sid updated for: %s from netconf" % node.name)
413438
elif 'sid-value' in temp_sid.keys():
414-
node_list[node_list.index(node)] = node._replace(sid = temp_sid['sid-value'])
415-
logging.info("SR sid updated for: %s" % node.name)
416-
else:
417-
logging.error("No sid value for: %s - %s" % (node.name,temp_sid))
418-
419-
node_configs, rid_dict = get_netconf()
420-
for node in node_list:
421-
temp_sid = node_configs.get(node.name,{})
422-
rid_sid = rid_dict.get(node.loopback,{})
423-
424-
if len(temp_sid) >0:
425-
update(node, temp_sid)
426-
elif len(rid_sid) >0:
427-
update(node, rid_sid)
439+
node_list[node_list.index(node)] = node._replace(sid=temp_sid['sid-value'])
440+
logging.info("SR sid updated for: %s from netconf" % node.name)
428441
else:
429-
logging.error("No sid for: %s" % node.name)
442+
logging.error("No sid value for: %s - %s" % (node.name, temp_sid))
443+
444+
# BGP Check
445+
bgp_rib = MyBGP()
446+
sid_dict = bgp_rib.get_sr_info()
447+
my_local_sids = file_to_dict(sid_saves)
448+
# sid_dict = {}
449+
if len(sid_dict) > 0:
450+
for node in node_list:
451+
if node.loopback in sid_dict.keys():
452+
node_list[node_list.index(node)] = node._replace(sid=sid_dict[node.loopback])
453+
logging.info('SR sid updated for: {} from bgp'.format(node.name))
454+
else:
455+
logging.error('No BGP SID for: {}'.format(node.name))
456+
else:
457+
node_configs, rid_dict = get_netconf()
458+
for node in node_list:
459+
temp_sid = node_configs.get(node.name,{})
460+
rid_sid = rid_dict.get(node.loopback,{})
461+
462+
if len(temp_sid) >0:
463+
update(node, temp_sid)
464+
elif len(rid_sid) >0:
465+
update(node, rid_sid)
466+
elif my_local_sids.get(node.loopback):
467+
node_list[node_list.index(node)] = node._replace(sid=my_local_sids[node.loopback]['sid'])
468+
logging.info('SR sid updated for: {} from static'.format(node.name))
469+
else:
470+
logging.error("No sid for: %s" % node.name)
430471

431472
return node_list
432473

@@ -1107,14 +1148,14 @@ def sort_paths(pathlist, metriclist, type):
11071148

11081149
def postUrl(url, data):
11091150
import requests
1110-
response = requests.post(url, data=data, auth = (odl_user, odl_password),headers= {'Content-Type': 'application/json'})
1151+
response = requests.post(url, data=data, auth=(odl_user, odl_password), headers={'Content-Type': 'application/json'})
11111152
# print response.text
11121153
return response.json()
11131154

11141155
def postXml(url, data):
11151156
""" post our lsp creation commands """
11161157
import requests
1117-
response = requests.post(url, data=data, auth = (odl_user, odl_password),headers= {'Content-Type': 'application/xml'})
1158+
response = requests.post(url, data=data, auth=(odl_user, odl_password), headers={'Content-Type': 'application/xml'})
11181159
# print response.text
11191160

11201161
return response.json()

0 commit comments

Comments
 (0)