diff --git a/projects/aloe/src/python/aloe/__main__.py b/projects/aloe/src/python/aloe/__main__.py index 134f310..2bde754 100644 --- a/projects/aloe/src/python/aloe/__main__.py +++ b/projects/aloe/src/python/aloe/__main__.py @@ -9,15 +9,16 @@ when packet delivery latencies radically degrade and maintain a report file. import argparse from collections import defaultdict, deque from datetime import datetime, timedelta +from itertools import cycle import logging from multiprocessing import Process, Queue import queue import sys from typing import List -from .lib import * - import graphviz +from icmplib import Hop, ping, traceroute +from icmplib.utils import * import requests @@ -27,24 +28,12 @@ parser = argparse.ArgumentParser() parser.add_argument("hosts", nargs="+") -def distinct(iter): - s = set() - l = [] - for e in iter: - if e in s: - continue - else: - l.append(e) - s.add(e) - return l - - class Topology(object): LOCALHOST = Hop("127.0.0.1", 1, [0.0], 0) def __init__(self): - self._graph = defaultdict(set) # Dict[address, List[address]] - self._nodes = {self.LOCALHOST.address: self.LOCALHOST} # Dict[address, Host] + self._graph = defaultdict(set) + self._nodes = {self.LOCALHOST.address: self.LOCALHOST} def next_hops(self, address: str) -> List[str]: return list(self._graph.get(address)) @@ -60,9 +49,11 @@ class Topology(object): e0 = self._nodes[e.address] e0._packets_sent += e._packets_sent e0._rtts.extend(e.rtts) - e0._distance = max(e.distance, e0.distance) + e0._distance = min(e.distance, e0.distance) - hosts = [(self.LOCALHOST.address, self.LOCALHOST.distance)] + [(e.address, e.distance) for e in trace] + hosts = [(self.LOCALHOST.address, self.LOCALHOST.distance)] + [ + (e.address, e.distance) for e in trace + ] i1 = iter(hosts) i2 = iter(hosts) next(i2) @@ -78,8 +69,10 @@ class Topology(object): g.edge(n.address, next) # Lol. Lmao. - return requests.post("https://dot-to-ascii.ggerganov.com/dot-to-ascii.php", params={"boxart":1, "src": g.source}).text - # return g.source + return requests.post( + "https://dot-to-ascii.ggerganov.com/dot-to-ascii.php", + params={"boxart": 1, "src": g.source}, + ).text def __iter__(self): return iter(sorted(self._nodes.values(), key=lambda n: n.distance)) @@ -88,6 +81,7 @@ class Topology(object): del self._graph[key] del self._nodes[key] + def compute_topology(hostlist, topology=None): """Walk a series of traceroute tuples, computing a 'worst expected latency' topology from them.""" @@ -101,12 +95,6 @@ def compute_topology(hostlist, topology=None): return topology -def cycle(iter): - while True: - for e in iter: - yield e - - def pinger(host, id, queue): # Mokney patch the RTT tracking host._rtts = deque(host._rtts, maxlen=100) @@ -143,6 +131,7 @@ if __name__ == "__main__": spinner = cycle("|/-\\") with open("incidents.txt", "a") as fp: + fp.write("RESTART\n") while True: now = datetime.now() @@ -157,7 +146,7 @@ if __name__ == "__main__": log.info("Graph -\n" + topology.render()) for h in topology: - if h.distance < 1 or h.distance > 6: + if h.distance == 0: continue if h.address in workers: @@ -176,7 +165,11 @@ if __name__ == "__main__": elif res.is_alive: if last and (delta := timestamp - last) > recovered_duration: - fp.write(f"RECOVERED\t{res.address}\t{timestamp.isoformat()}\t{delta.total_seconds()}\n") + fp.write( + f"RECOVERED\t{res.address}\t{timestamp.isoformat()}\t{delta.total_seconds()}\n" + ) + elif not last: + fp.write(f"UP\t{res.address}\t{timestamp.isoformat()}\n") last_seen[res.address] = timestamp elif not res.is_alive: @@ -184,7 +177,9 @@ if __name__ == "__main__": workers[h.address].terminate() del workers[h.address] del topology[h.address] - fp.write(f"DEAD\t{res.address}\t{timestamp.isoformat()}\t{delta.total_seconds()}\n") + fp.write( + f"DEAD\t{res.address}\t{timestamp.isoformat()}\t{delta.total_seconds()}\n" + ) else: fp.write(f"DOWN\t{res.address}\t{timestamp.isoformat()}\n") diff --git a/projects/aloe/src/python/aloe/lib.py b/projects/aloe/src/python/aloe/lib.py deleted file mode 100644 index e04c1fd..0000000 --- a/projects/aloe/src/python/aloe/lib.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python3 - -import logging -from time import sleep - -from icmplib import Hop -from icmplib.exceptions import ( - ICMPLibError, - TimeExceeded, -) -from icmplib.models import Hop, ICMPRequest -from icmplib.sockets import ( - ICMPv4Socket, - ICMPv6Socket, -) -from icmplib.utils import * - - -log = logging.getLogger(__name__) - - -def urping(address: str, hops: int, via: str, family=None, count=3, fudge=4, interval=0.05, timeout=2, source=None, **kwargs) -> Hop: - """Ur-ping by (ab)using ICMP TTLs. - - Craft an ICMP message which would go one (or more) hops FARTHER than the `address` host, routed towards `via`. - Send `count * fudge` packets, looking for responses from `address`. - Responses from `address` are considered; and a `Hop` is built from those results. - Other responses from other hosts are discarded. - - """ - - if is_hostname(via): - via = resolve(via, family)[0] - - if is_hostname(address): - address = resolve(address, falmiy)[0] - - if is_ipv6_address(via): - _Socket = ICMPv6Socket - else: - _Socket = ICMPv4Socket - - ttl = hops - hop = Hop(address, 0, []) - packets_sent = 0 - - with _Socket(source) as sock: - for _ in range(count * fudge): - request = ICMPRequest( - destination=via, - # Note that we act like this is a new stream with a new ID and sequence each time to try and fool multipathing. - id=unique_identifier(), - sequence=0, - ttl=ttl, - **kwargs) - - try: - sock.send(request) - packets_sent += 1 - - reply = None - reply = sock.receive(request, timeout) - rtt = (reply.time - request.time) * 1000 - - reply.raise_for_status() - - except TimeExceeded: - sleep(interval) - - except ICMPLibError as e: - log.exception(e) - break - - if not reply: - log.debug(f"got no reply from {address}") - else: - # Because the default repr() is crap - reply_str = "ICMPReply({})".format(", ".join(slot + "=" + str(getattr(reply, slot)) for slot in reply.__slots__)) - log.debug(f"Pinging {address} (distance {hops}) via {via} got reply {reply_str}") - - if reply and reply.source != address: - continue - - elif reply: - hop._packets_sent += 1 - hop._rtts.append(rtt) - - if hop and hop._packets_sent >= count: - break - - return hop