Skip to content
Permalink
main
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
#!/usr/bin/env python3
from jnpr.junos import Device
from jnpr.junos.op.phyport import PhyPortTable
from getpass import getpass
from lxml import etree
from pathlib import Path
import json
import os
import re
import logging
import sys
logger = logging.getLogger("junos-routes")
logging.basicConfig(format='%(asctime)s %(levelname)-8s %(message)s')
logger.setLevel(logging.DEBUG)
def main():
h = sys.argv[1] # Router management address
pw = getpass("Password: ")
dev = Device(
host=h,
user=os.getenv('USER'),
passwd=pw
)
logger.info(f"Connecting to device: {h}")
dev.open()
# get-route-information
logger.debug(f"Facts: {dev.facts!r}")
# Get interface information
logger.info("Fetching interface information")
intf = dev.rpc.get_interface_information(dev_timeout=60)
(Path("route-snapshot-debug") / f"{h}-interfaces.xml" ).write_text(etree.tostring(intf, encoding='unicode', pretty_print=True))
# Get all logical interface information
intf_by_addr = {} # single-ip => {name: ..., prefix: ...}
for logical_intf in intf.findall("./physical-interface/logical-interface"):
name = logical_intf.findtext("./name").strip()
for afi in logical_intf.findall("./address-family"):
family = afi.findtext(".//address-family-name").strip()
if family not in ("inet", "inet6"): continue
local_addr = afi.findtext(".//ifa-local") # 192.168.0.1
net = afi.findtext(".//ifa-destination") # 192.168.0.0/24, None if loopback
if local_addr is None or net is None:
continue
local_addr = local_addr.strip()
net = net.strip()
prefix = local_addr + "/" + net.split("/")[1]
intf_by_addr[local_addr] = {"name": name, "prefix": prefix}
# Get neighbor info
neighbors = []
logger.info(f"Fetching neighbors")
rpc_nei = dev.rpc.get_bgp_neighbor_information()
for nei in rpc_nei.findall('.//bgp-peer'):
state = nei.findtext('./peer-state')
vrf = nei.findtext('./peer-cfg-rti')
local_addr = nei.findtext('./local-address')
local_addr = local_addr.split('+')[0]
remote_addr = nei.findtext('./peer-address')
remote_addr = remote_addr.split('+')[0]
local_as = nei.findtext('./local-as')
remote_as = nei.findtext('./peer-as')
descr = nei.findtext('./description')
intf_config = intf_by_addr.get(local_addr, {'name': "UNKNOWN INTF", 'prefix': '0.0.0.0/0'})
if vrf == 'master':
vrf = 'RE'
nei_info = {
'vrf': vrf,
'local_addr': local_addr,
'local_as': local_as,
'remote_addr': remote_addr,
'remote_as': remote_as,
'description': descr,
'intf_prefix': intf_config['prefix'],
}
# Skip iBGP
if local_as == "11537" and remote_as == "11537":
continue
# Skip things that aren't in a connected state
if state != 'Established':
continue
neighbors.append(nei_info)
# print(etree.tostring(reply, encoding='unicode', pretty_print=True))
# routes = dev.rpc.get_route_information(table='inet.0', dev_timeout=60)
# Get neighbors
logger.info("Looping through neighbors")
for nei in neighbors:
logger.info(f"Fetching received routes for {nei['vrf']} - {nei['remote_addr']} - {nei['description']}")
# Fetch routes
rpc_received = dev.rpc.get_route_information(
receive_protocol_name="bgp",
peer=nei['remote_addr'], # Change to the peer address
level="extensive",
all="",
dev_timeout=60
)
# Save to JSON
routes = []
for rt in rpc_received.findall('.//route-table/rt'):
dest = rt.findtext('./rt-destination')
# Skip if it contains both a . and a :, which means it's an l3vpn
# this is a super lazy way of filtering out l3vpn routes, and I'm
# not sure this is fool proof
if re.match(r'.*[.].*[:].*', dest):
continue
prefix_len = rt.findtext('./rt-prefix-length')
prefix = f'{dest}/{prefix_len}'
as_path = rt.findtext('.//attr-as-path-effective/attr-value')
# aggregator_as = rt.findtext('.//attr-aggregator/aggr-as-number')
# aggregator_rid = rt.findtext('.//attr-aggregator/aggr-router-id')
communities = [c.text for c in rt.findall('.//communities/community')]
med = int(rt.findtext('.//med') or 0)
# Cleanup AS-Path
as_path = re.sub(r' [IE?]$', '', as_path)
# as_path = [int(asn) for asn in as_path.split(" ")[:-1]]
# as_path = [asn for asn in as_path.split(" ")[:-1]]
route = {
'prefix': prefix,
'communities': communities,
'as-path': as_path,
'med': med,
}
logger.debug(f" got route {route!r}")
routes.append(route)
base = f"{nei['remote_addr']}-{nei['vrf']}"
# Save debug info
(Path("route-snapshot-debug") / f'{base}-routes.xml').write_text(etree.tostring(rpc_received, encoding='unicode', pretty_print=True))
# Save BGP neighbor information (neighbor info + routes)
peer_data = nei.copy()
peer_data['routes'] = routes
(Path("route-snapshot") / f'{base}.json').write_text(json.dumps(peer_data, sort_keys=True, indent=2))
# (Path("route-snapshot") / f'{base}-info.json').write_text(json.dumps(nei, sort_keys=True, indent=2))
# (Path("route-snapshot") / f'{base}-routes.json').write_text(json.dumps(routes, sort_keys=True, indent=2))
logger.info("DONE")
if __name__ == "__main__":
main()