#!/usr/bin/env python # # Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # # Examples # # - get the state of neighbors # $ gobgpcli show neighbors # - get the state of a neighbor # $ gobgpcli show neighbor 10.0.0.2 # - get the local rib of a neighbor # $ gobgpcli show neighbor 10.0.0.2 local from optparse import OptionParser import requests import sys import inspect import json from datetime import timedelta class Show(object): def __init__(self, options, args): super(Show, self).__init__() self.options = options self.args = args self.base_url = self.options.url + ":" + str(self.options.port) + "/v1/bgp" def __call__(self): if len(self.args) == 0: return 1 for f in inspect.getmembers(self, inspect.ismethod): func_name = f[0] if func_name == "do_" + self.args[0]: return f[1]() return 1 def _neighbors(self, neighbor=None): capdict = {1: "MULTIPROTOCOL", 2: "ROUTE_REFRESH", 4: "CARRYING_LABEL_INFO", 64: "GRACEFUL_RESTART", 65: "FOUR_OCTET_AS_NUMBER", 70: "ENHANCED_ROUTE_REFRESH", 128: "ROUTE_REFRESH_CISCO"} r = requests.get(self.base_url + "/neighbors") neighbors = r.json() if self.options.debug: print neighbors return 0 for n in sorted(neighbors, key=lambda n: n["conf"]["remote_ip"]): if neighbor is not None and neighbor != n["conf"]["remote_ip"]: continue print("BGP neighbor is {:s}, remote AS {:d}".format(n["conf"]["remote_ip"], n["conf"]["remote_as"])) print(" BGP version 4, remote router ID {:s}".format(n["conf"]["id"])) print(" BGP state = {:s}, up for {:s}".format(n["info"]["bgp_state"], str(timedelta(seconds=n["info"]["uptime"])))) print(" Neighbor capabilities:") for i in n["conf"]["CapList"]: if i in capdict: i = capdict[i] else: i = "UNKNOWN (" + str(i) + ")" print(" {:s}".format(i)) print(" Message statistics:") print(" Sent Rcvd") print(" Opens: {:>10d} {:>10d}".format(n["info"]["open_message_out"], n["info"]["open_message_in"])) print(" Notifications: {:>10d} {:>10d}".format(n["info"]["notification_out"], n["info"]["notification_in"])) print(" Updates: {:>10d} {:>10d}".format(n["info"]["update_message_out"], n["info"]["update_message_in"])) print(" Keepalives: {:>10d} {:>10d}".format(n["info"]["keepalive_message_out"], n["info"]["keepalive_message_in"])) print(" Route Refesh: {:>10d} {:>10d}".format(n["info"]["refresh_message_out"], n["info"]["refresh_message_in"])) print(" Total: {:>10d} {:>10d}".format(n["info"]["total_message_out"], n["info"]["total_message_in"])) print("") return 0 def do_neighbors(self): if len(self.args) != 1: return 1 return self._neighbors() def _format_attrs(self, attrlist): attrs = [] for a in attrlist: if a["Type"] == "BGP_ATTR_TYPE_NEXT_HOP": pass elif a["Type"] == "BGP_ATTR_TYPE_AS_PATH": pass elif a["Type"] == "BGP_ATTR_TYPE_ORIGIN": attrs.append({"Origin": a["Value"]}) elif a["Type"] == "BGP_ATTR_TYPE_MULTI_EXIT_DISC": attrs.append({"Med": a["Metric"]}) elif a["Type"] == "BGP_ATTR_TYPE_LOCAL_PREF": attrs.append({"LocalPref": a["Pref"]}) elif a["Type"] == "BGP_ATTR_TYPE_ATOMIC_AGGREGATE": attrs.append("AtomicAggregate") elif a["Type"] == "BGP_ATTR_TYPE_AGGREGATE": attrs.append({"Aggregate": {"AS": a["AS"], "Address": a["Address"]}}) elif a["Type"] == "BGP_ATTR_TYPE_COMMUNITIES": wellknown = { 0xffff0000: "planned-shut", 0xffff0001: "accept-own", 0xffff0002: "ROUTE_FILTER_TRANSLATED_v4", 0xffff0003: "ROUTE_FILTER_v4", 0xffff0004: "ROUTE_FILTER_TRANSLATED_v6", 0xffff0005: "ROUTE_FILTER_v6", 0xffff0006: "LLGR_STALE", 0xffff0007: "NO_LLGR", 0xFFFFFF01: "NO_EXPORT", 0xFFFFFF02: "NO_ADVERTISE", 0xFFFFFF03: "NO_EXPORT_SUBCONFED", 0xFFFFFF04: "NOPEER"} l = [] for v in a["Value"]: if v in wellknown: l.append(wellknown[v]) else: l.append(str((0xffff0000 & v) >> 16) + ":" + str(0xffff & v)) attrs.append({"Community": l}) elif a["Type"] == "BGP_ATTR_TYPE_ORIGINATOR_ID": attrs.append({"Originator": a["Address"]}) elif a["Type"] == "BGP_ATTR_TYPE_CLUSTER_LIST": attrs.append({"Cluster": a["Address"]}) elif a["Type"] == "BGP_ATTR_TYPE_MP_REACH_NLRI": pass elif a["Type"] == "BGP_ATTR_TYPE_MP_UNREACH_NLRI": pass elif a["Type"] == "BGP_ATTR_TYPE_AS4_PATH": pass else: attrs.append({a["Type"]: a["Value"]}) return attrs def do_neighbor(self): if len(self.args) != 2 and len(self.args) != 3: return 1 if len(self.args) == 2: return self._neighbors(neighbor=self.args[1]) if self.args[2] == "local": self.args[2] = "local-rib" r = requests.get(self.base_url + "/neighbor/" + self.args[1] + "/" + self.args[2]) if self.options.debug: print r.json() return 0 print(" Network Next Hop AS Attrs") for d in r.json()["Destinations"]: for p in d["Paths"]: nexthop = "" AS = "" for a in p["Attrs"]: if a["Type"] == "BGP_ATTR_TYPE_NEXT_HOP": nexthop = a["Nexthop"] elif a["Type"] == "BGP_ATTR_TYPE_AS_PATH": AS = a["AsPath"] if p["Best"] == "true": header = "*>" else: header = "*" print("{:s} {:s} {:s} {:s} {:s}".format(header, p["Network"], nexthop, AS, self._format_attrs(p["Attrs"]))) return 0 def main(): usage = "gobpgcli [options] " parser = OptionParser(usage) parser.add_option("-u", "--url", dest="url", default="http://localhost", help="specifying an url (http://localhost by default)") parser.add_option("-p", "--port", dest="port", default=8080, help="specifying a port (8080 by default)") parser.add_option("-d", "--debug", dest="debug", action="store_true", help="dump raw json") (options, args) = parser.parse_args() commands = {"show": Show} if len(args) == 0: parser.print_help() sys.exit(1) if args[0] not in commands: parser.print_help() sys.exit(1) ret = commands[args[0]](options, args[1:])() if ret != 0: parser.print_help() sys.exit(1) if __name__ == "__main__": main()