X-Git-Url: https://wannabe.guru.org/gitweb/?a=blobdiff_plain;f=arper.py;h=c187023c98798044ac27e2b888f70ded7275ac93;hb=9bfba844fe0efe6bbf36882d7a56ef41da6d61b9;hp=2fc07674eedd0b8206586323ccb03bdea240fb33;hpb=eb9e6df32ed696158bf34dba6464277b648f5c74;p=python_utils.git diff --git a/arper.py b/arper.py index 2fc0767..c187023 100644 --- a/arper.py +++ b/arper.py @@ -33,7 +33,7 @@ cfg.add_argument( cfg.add_argument( '--arper_cache_max_staleness', type=argparse_utils.valid_duration, - default=datetime.timedelta(seconds=60 * 60), + default=datetime.timedelta(seconds=60 * 15), metavar='DURATION', help='Max acceptable age of the kernel arp table cache' ) @@ -48,7 +48,7 @@ cfg.add_argument( @persistent.persistent_autoloaded_singleton() class Arper(persistent.Persistent): def __init__( - self, cached_state: Optional[BiDict[str, str]] = None + self, cached_state: Optional[BiDict] = None ) -> None: self.state = BiDict() if cached_state is not None: @@ -56,19 +56,44 @@ class Arper(persistent.Persistent): self.state = cached_state else: logger.debug('No usable cached state; calling /usr/sbin/arp') - self.update() + self.update_from_arp_scan() + self.update_from_arp() + if len(self.state) < config.config['arper_min_entries_to_be_valid']: + raise Exception('Arper didn\'t find enough entries; only got {len(self.state)}.') - def update(self): - output = exec_utils.cmd( - '/usr/sbin/arp -a', - timeout_seconds=5.0 - ) + def update_from_arp_scan(self): + network_spec = site_config.get_config().network + try: + output = exec_utils.cmd( + f'/usr/local/bin/arp-scan --retry=6 --timeout 350 --backoff=1.4 --random --numeric --plain --ignoredups {network_spec}', + timeout_seconds=10.0 + ) + except Exception as e: + logger.exception(e) + return for line in output.split('\n'): ip = string_utils.extract_ip_v4(line) mac = string_utils.extract_mac_address(line) - if ip is not None and mac is not None: + if ip is not None and mac is not None and mac != 'UNKNOWN' and ip != 'UNKNOWN': mac = mac.lower() - logger.debug(f' {mac} => {ip}') + logger.debug(f'ARPER: {mac} => {ip}') + self.state[mac] = ip + + def update_from_arp(self): + try: + output = exec_utils.cmd( + '/usr/sbin/arp -a', + timeout_seconds=10.0 + ) + except Exception as e: + logger.exception(e) + return + for line in output.split('\n'): + ip = string_utils.extract_ip_v4(line) + mac = string_utils.extract_mac_address(line) + if ip is not None and mac is not None and mac != 'UNKNOWN' and ip != 'UNKNOWN': + mac = mac.lower() + logger.debug(f'ARPER: {mac} => {ip}') self.state[mac] = ip def get_ip_by_mac(self, mac: str) -> Optional[str]: @@ -78,23 +103,6 @@ class Arper(persistent.Persistent): def get_mac_by_ip(self, ip: str) -> Optional[str]: return self.state.inverse.get(ip, None) - @overrides - def save(self) -> bool: - if len(self.state) > config.config['arper_min_entries_to_be_valid']: - logger.debug( - f'Persisting state to {config.config["arper_cache_location"]}' - ) - with file_utils.FileWriter(config.config['arper_cache_location']) as wf: - for (mac, ip) in self.state.items(): - mac = mac.lower() - print(f'{mac}, {ip}', file=wf) - return True - else: - logger.warning( - f'Only saw {len(self.state)} entries; needed at least {config.config["arper_min_entries_to_be_valid"]} to bother persisting.' - ) - return False - @classmethod @overrides def load(cls) -> Any: @@ -122,6 +130,22 @@ class Arper(persistent.Persistent): f'{cache_file} sucks, only {len(cached_state)} entries. Deleting it.' ) os.remove(cache_file) - logger.debug('No usable saved state found') return None + + @overrides + def save(self) -> bool: + if len(self.state) > config.config['arper_min_entries_to_be_valid']: + logger.debug( + f'Persisting state to {config.config["arper_cache_location"]}' + ) + with file_utils.FileWriter(config.config['arper_cache_location']) as wf: + for (mac, ip) in self.state.items(): + mac = mac.lower() + print(f'{mac}, {ip}', file=wf) + return True + else: + logger.warning( + f'Only saw {len(self.state)} entries; needed at least {config.config["arper_min_entries_to_be_valid"]} to bother persisting.' + ) + return False