Source code for net.api

# -*- coding: utf-8 -*-
"""
api module
----------

Contains the general network interactions for net.
"""

# std imports
import re
import math
import threading
import subprocess

# package imports
import net

# local imports
from net.imports import ConnectionRefusedError, PermissionError

__all__ = [
    'peers'
]


# threading
LOCK = threading.Lock()
IP_REGEX = re.compile(r'\d+\.\d+\.\d\.\d+')


# cache
PEERS = None


[docs]def peers(refresh=False, groups=None, on_host=False, hubs_only=False): """ Get a list of all peers on your network. This is a cached values since the call to graph the network can be long. You can also limit this search to only look for operating peers on the localhost which does not require the long network scan, just set the ``on_host`` kwarg to True. Hubs act as the centers for certain application events or processes. In some cases, you may only want to subscribe or communicate with hubs. You can specify this through the ``hubs_only`` kwarg. The initial call to this will hang for a few seconds. Under the hood, it is making a shell call to ``arp -a`` which will walk your network and find all hosts. Standard call to get the peers on your network. .. code-block:: python all_peers = net.peers() Only search for peers on local host and not on the network. .. code-block:: python all_peers = net.peers(on_host=True) Refresh all peers in the cache .. code-block:: python all_peers = net.peers(refresh=True) Refresh the cache with peers in group1 .. code-block:: python all_peers = net.peers("group1", refresh=True) Refresh the cache with peers in group1 and 2 .. code-block:: python all_peers = net.peers(["group1", "group2"], refresh=True) Refresh the cache with all of the hubs on the network regardless of group. .. code-block:: python all_peers = net.peers(hubs_only=True, refresh=True) Refresh the cache with only hubs in group1 and 2 .. code-block:: python all_peers = net.peers(["group1", "group2"], hubs_only=True, refresh=True) :param refresh: Bool :param groups: str :param on_host: Bool :param hubs_only: Bool :return: { # Peers 'peers': { b'MTkyLjE2OC4yLjI0OjMwMTAgLT4gTm9uZQ==': { 'group': 'None', 'host': '192.168.2.24', 'port': 3010, 'hub': False, 'executable': path/to/executable, 'user': username }, }, # Groups 'None': [ b'MTkyLjE2OC4yLjI0OjMwMTAgLT4gTm9uZQ==' ] } """ if PEERS is None or refresh: get_peers(groups, on_host, hubs_only) return PEERS
def local_network(): """ Runs ``arp -a`` to get all hosts. :return: list of ip address on the local network """ raw_output = bytes(subprocess.check_output('arp -a', shell=True)).decode('ascii') return IP_REGEX.findall(raw_output) def find_peers_in_block(ips, groups=None, hubs_only=False): """ Sniffs out peers in the defined group based on the list of ip's :param ips: list of ip addresses :param groups: the list of groups you'd like to filter with. Defaults to the same as the current peer. :return: List of peer addresses """ global PEERS # pull in the local peer peer = net.Peer() if not groups: groups = [peer.group] # loop over all the addresses for address in ips: # loop over ports for port in peer.ports(): # skip self if port == peer.port and address == peer.host: continue # generate the peer foreign_peer_id = peer.generate_id(port, address) try: # ping the peer and if it responds with the proper info, # register it. Shut off the logger for this so we dont spam # the console. net.LOGGER.disabled = True info = net.info(peer=foreign_peer_id, time_out=0.1) net.LOGGER.disabled = False # skip registering this if the info is already in the # registry. if info in PEERS['peers'].values(): continue # filter out peers that aren't in the groups requested. if info['group'] not in groups: continue # filter out non-hubs if that is what was requested if hubs_only and not info['hub']: continue # acquire the lock and register LOCK.acquire() # register with the general information per peer PEERS['peers'][foreign_peer_id] = info # register with the group registry group_registry = PEERS.setdefault(info['group'], []) group_registry.append(foreign_peer_id) # release the shared resource LOCK.release() except (PermissionError, ConnectionRefusedError, OSError): net.LOGGER.disabled = False def get_peers(groups, on_host, hubs_only): """ Get a list of all valid remote peers. :param groups: List of groups :param on_host: Search only localhost :param hubs_only: Get Hubs only :return: List of peer addresses """ global PEERS PEERS = { 'peers': {} } # get this peer for pinging peer = net.Peer() # create subnet network = [net.HOST_IP] if not on_host: network = local_network() # logging help total_hosts = len(network) total_ports = len(peer.ports()) net.LOGGER.debug( "Calculated network sweep: {0} hosts X {1} ports = {2} pings".format( total_hosts, total_ports, total_hosts * total_ports ) ) # skip the threading integration if the environment does not call for it. if net.THREAD_LIMIT <= 0: return find_peers_in_block(network, groups, hubs_only) # calculate thread chunk. There should always be at least one thread chunk thread_chunks = max(int(math.ceil(total_hosts/net.THREAD_LIMIT)), 1) # loop over and spawn threads start = 0 threads = [] for chunk in range(0, net.THREAD_LIMIT): end = start + thread_chunks thread = threading.Thread( target=find_peers_in_block, args=(network[start:end], groups, hubs_only) ) thread.setName("Network_Scanner_" + str(chunk)) thread.daemon = True threads.append(thread) thread.start() start = end # wait for all worker threads to finish for thread in threads: thread.join() return PEERS