134 lines
5.1 KiB
Python

###
# Copyright (c) 2021, James Lu
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import json
import urllib.parse
# local modules
from . import parsebird, parsetrace
# 3rd party
import requests
from supybot import utils, plugins, callbacks
from supybot.commands import wrap
try:
from supybot.i18n import PluginInternationalization
_ = PluginInternationalization('BirdLGGo')
except ImportError:
# Placeholder that allows to run the plugin on a bot
# without the i18n module
_ = lambda x: x
class BirdLGGo(callbacks.Plugin):
"""API client (show route / traceroute) for Bird-lg-go"""
threaded = True
def lg_post_request(self, irc, msg, query):
url = urllib.parse.urljoin(self.registryValue("lgServer", network=irc.network, channel=msg.channel), "/api/")
if not url:
raise irc.error("No looking glass server specified - please set plugins.birdlggo.lgserver", Raise=True)
elif not query.get("servers"):
raise irc.error("No target nodes specified - please set plugins.birdlggo.nodes", Raise=True)
req = requests.post(url, json=query)
resp = req.json()
if resp.get("error"):
raise irc.error("Error from looking glass: " + resp["error"], Raise=True)
else:
return resp["result"]
@wrap(['something'])
def traceroute(self, irc, msg, args, target):
"""<target>
Sends a traceroute to the target host or IP.
"""
nodes = self.registryValue("nodes", network=irc.network, channel=msg.channel)
query = {
"servers": nodes,
"type": "traceroute",
"args": target
}
results = self.lg_post_request(irc, msg, query)
hop_display_mode = self.registryValue("traceHopFormat", network=irc.network, channel=msg.channel)
def _format_tracehop(hop):
if hop_display_mode == 'ip' or hop.ip == hop.ptr:
return hop.ip
elif hop_display_mode == 'ptr':
return hop.ptr
elif hop_display_mode == 'both':
return f'{hop.ptr}[{hop.ip}]'
irc.error("Unknown traceHopFormat setting %r" % hop_display_mode)
for result in results:
parsed_result = parsetrace.parse_traceroute(result["data"])
server = result["server"]
hops = " ".join(map(_format_tracehop, parsed_result.hops))
latency = parsed_result.latency or "(timed out)"
notes = ""
if parsed_result.notes:
notes = "- " + ", ".join(parsed_result.notes)
irc.reply(f"{server} -> {target}: {latency} | {hops} {notes}")
@wrap(['something'])
def showroute(self, irc, msg, args, target):
"""<target>
Shows the preferred BIRD route for the given target.
"""
nodes = self.registryValue("nodes", network=irc.network, channel=msg.channel)
query = {
"servers": nodes,
"type": "bird",
"args": f"show route for {target} all primary"
}
results = self.lg_post_request(irc, msg, query)
for result in results:
parsed_result = parsebird.parse_bird(result["data"])
server = result["server"]
s = f"{server} -> {target}: {parsed_result.route_type} {parsed_result.via} [Type: {parsed_result.route_origin}] [Pref: {parsed_result.route_preference}]"
if parsed_result.bgp_as_path:
path = " ".join(parsed_result.bgp_as_path)
s += f" [AS path: {path}]"
irc.reply(s)
Class = BirdLGGo
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=120: