#!/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 # - reset # $ gobgpcli reset neighbor 10.0.0.2 # - softresetin # $ gobgpcli softresetin neighbor 10.0.0.2 # - softresetout # $ gobgpcli softresetout neighbor 10.0.0.2 # - shutdown # $ gobgpcli shutdown neighbor 10.0.0.2 from optparse import OptionParser import requests import sys import inspect import json from datetime import timedelta class Action(object): def __init__(self, command, options, args): super(Action, self).__init__() self.command = command 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) != 2: return 1 r = requests.post(self.base_url + "/neighbor/" + self.args[1] + "/" + self.command) if r.status_code == requests.codes.ok: print "Succeed" else: print "Failed" return 0 class Show(object): def __init__(self, _command, 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 _neighbor(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(" BGP OutQ = {:d}, Flops = {:d}".format(n["info"]["OutQ"], n["info"]["Flops"])) print(" Neighbor capabilities:") allcap = set(n["conf"]["RemoteCap"]) | set(n["conf"]["LocalCap"]) for i in sorted(allcap): if i in capdict: k = capdict[i] else: k = "UNKNOWN (" + str(i) + ")" r = "" if i in n["conf"]["LocalCap"]: r += "advertised" if i in n["conf"]["RemoteCap"]: if len(r) != 0: r += " and " r += "received" print(" {:s}: {:s}".format(k, r)) 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 @staticmethod def format_timedelta(sec): days = timedelta(seconds=sec).days t = timedelta(seconds=timedelta(seconds=sec).seconds) if days == 0: up = t else: up = str(days) + "d" + " " + t return up def do_neighbors(self): if len(self.args) != 1: return 1 r = requests.get(self.base_url + "/neighbors") neighbors = r.json() if self.options.debug: print neighbors return 0 print("{:40s} {:5s} {:11s}{:11s} {:8s} |#{:8s} {:8s} {:8s}".format("Peer", "AS", "Up", "Down", "State", "Advertised", "Received", "Accepted")) for n in sorted(neighbors, key=lambda n: n["conf"]["remote_ip"]): if n["info"]["uptime"] == 0: up = "never" else: up = self.format_timedelta(n["info"]["uptime"]) down = self.format_timedelta(n["info"]["downtime"]) s = n["info"]["bgp_state"] if s == "BGP_FSM_IDLE": state = "Idle" elif s == "BGP_FSM_CONNECT": state = "Connect" elif s == "BGP_FSM_ACTIVE": state = "Active" elif s == "BGP_FSM_OPENSENT": state = "Sent" elif s == "BGP_FSM_OPENCONFIRM": state = "Confirm" elif s == "BGP_FSM_ESTABLISHED": state = "Establ" print("{:40s} {:5d} {:11s}{:11s} {:8s} | {:>8d} {:>8d} {:>8d}".format(n["conf"]["remote_ip"], n["conf"]["remote_as"], up, down, state, n["info"]["Advertized"], n["info"]["Received"], n["info"]["Accepted"])) return 0 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._neighbor(neighbor=self.args[1]) if self.args[2] == "local": self.args[2] = "local-rib" if self.args[2] == "received-routes": self.args[2] = "adj-rib-in" if self.args[2] == "advertised-routes": self.args[2] = "adj-rib-out" r = requests.get(self.base_url + "/neighbor/" + self.args[1] + "/" + self.args[2]) if self.options.debug: print r.json() return 0 timestamp = True if self.args[2] == "adj-rib-out": timestamp = False if timestamp: f = "{:2s} {:18s} {:15s} {:10s} {:10s} {:s}" print(f.format("", "Network", "Next Hop", "AS_PATH", "Time", "Attrs")) else: f = "{:2s} {:18s} {:15s} {:10s} {:s}" print(f.format("", "Network", "Next Hop", "AS_PATH", "Attrs")) if self.args[2] == "local-rib": for d in r.json()["Destinations"]: d["Paths"][d["BestPathIdx"]]["Best"] = True self.show_routes(f, d["Paths"], True, True) elif self.args[2] == "adj-rib-in" or self.args[2] == "adj-rib-out": rfs = ["RF_IPv4_UC", "RF_IPv6_UC"] for rf in rfs: paths = r.json()[rf] self.show_routes(f, paths, False, timestamp) return 0 def show_routes(self, f, paths, showBest=False, timestamp=False): for p in paths: nexthop = "" AS = "" for a in p["Attrs"]: if a["Type"] == "BGP_ATTR_TYPE_AS_PATH": AS = a["AsPath"] if showBest: if "Best" in p: header = "*>" else: header = "*" else: header = "" if timestamp: print(f.format(header, p["Network"], p["Nexthop"], AS, self.format_timedelta(p["Time"]), self._format_attrs(p["Attrs"]))) else: print(f.format(header, p["Network"], p["Nexthop"], AS, self._format_attrs(p["Attrs"]))) 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, "reset": Action, "shutdown": Action, "softreset": Action, "softresetin": Action, "softresetout": Action} 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]](args[0], options, args[1:])() if ret != 0: parser.print_help() sys.exit(1) if __name__ == "__main__": main()