def test_is_exit_node(): NODE_SETTINGS['use_exitnode'].append('beefea68e6') res = is_exit_node('beefea68e6') assert res is True NODE_SETTINGS['use_exitnode'].remove('beefea68e6') res = is_exit_node('beefea68e6') assert res is False
def get_target_node_id(node_lst, boot_lst): """ Return a target node ID from the active network to use as an insertion point for all the nodes in the bootstrap list. :notes: choice of tgt node is random; this may change :param trie: net data trie :param node_lst: list of all active nodes :param boot_lst: list of bootstrap nodes :return: <str> node ID """ import random from node_tools.ctlr_funcs import is_exit_node return random.choice( [x for x in node_lst if x not in boot_lst and not is_exit_node(x)])
def get_neighbor_ids(trie, node_id): """ Given the node ID, get the payloads from the net trie and return the attached network and neighbor node IDs. :param trie: net data trie :param node_id: node ID to lookup :return: tuple of net and node IDs """ from node_tools.ctlr_funcs import ipnet_get_netcfg from node_tools.ctlr_funcs import is_exit_node from node_tools.ctlr_funcs import netcfg_get_ipnet from node_tools.helper_funcs import find_ipv4_iface node_list = [] key_list = [] src_net = None exit_net = None src_node = None exit_node = None for key in trie: if node_id in key: key_list.append(key[0:16]) node_list.append(trie[key]) if len(key_list) != 2 and (len(key_list) == 1 and not is_exit_node(node_id)): raise AssertionError('Node {} keys {} are invalid!'.format( node_id, key_list)) else: for key, data in zip(key_list, node_list): node_ip = data['ipAssignments'][0] ipnet = netcfg_get_ipnet(node_ip) netcfg = ipnet_get_netcfg(ipnet) gw_ip = find_ipv4_iface(netcfg.gateway[0]) if node_ip == gw_ip: src_net = key for node in trie.suffixes(src_net)[1:]: if node_id != node: src_node = node else: exit_net = key for node in trie.suffixes(exit_net)[1:]: if node_id != node: exit_node = node return src_net, exit_net, src_node, exit_node
def handle_wedged_nodes(trie, wdg_q, off_q): """ Use node ID in wedged queue to lookup the corresponding exit node ID and add it to the offline queue. This is the only way we currently have to remove a wedged neighbor (exit) node. """ from node_tools.ctlr_funcs import is_exit_node from node_tools.trie_funcs import get_wedged_node_id deduped = list(set(list(wdg_q))) for node_id in deduped: with wdg_q.transact(): clean_from_queue(node_id, wdg_q) wedged_node = get_wedged_node_id(trie, node_id) if wedged_node is not None: if not is_exit_node(wedged_node): with off_q.transact(): add_one_only(wedged_node, off_q)
def get_active_nodes(id_trie): """ Find all the currently active nodes except the exit node (search the ID trie). :notes: In this case the answer depends on when this runs (relative to the cmds in `netstate` runner). :param id_trie: ID state trie :return: list of node IDs (empty list if None) """ from node_tools.ctlr_funcs import is_exit_node node_list = [] for node in [ x for x in list(id_trie) if len(x) == 10 and not is_exit_node(x) ]: node_list.append(node) return node_list
def find_orphans(net_trie, id_trie): """ Find orphaned nodes/empty nets based on current trie data from ZT api. In this context, an orphan is: * an empty net ID * a node ID with a single net ID (except the exit node) * a node ID without any networks :param net_trie: datrie trie object :param id_trie: datrie trie object :return: tuple of lists (orphan net list, orphan node list) """ from node_tools.ctlr_funcs import is_exit_node orphan_nets = [] orphan_nodes = [] for net_id in [x for x in list(id_trie) if len(x) == 16]: mbr_list = net_trie.suffixes(net_id)[1:] if mbr_list == []: orphan_nets.append(net_id) logger.warning('CLEANUP: found empty net: {}'.format(net_id)) for node_id in [ x for x in list(id_trie) if len(x) == 10 and not is_exit_node(x) ]: net_list = [] for key in list(net_trie): if node_id in key: net_list.append((key[0:16], node_id)) if len(net_list) == 1: orphan_nets.append(net_list[0]) logger.warning('CLEANUP: found orphan net {}'.format(net_list)) elif len(net_list) == 0: orphan_nodes.append(node_id) logger.warning('CLEANUP: found orphan node {}'.format(node_id)) return orphan_nets, orphan_nodes
async def main(): """State cache updater to retrieve data from a local ZeroTier node.""" async with aiohttp.ClientSession() as session: ZT_API = get_token() client = ZeroTier(ZT_API, loop, session) try: # handle offline/wedged nodes handle_wedged_nodes(ct.net_trie, wdg_q, off_q) pre_off = list(off_q) logger.debug('{} nodes in offline queue: {}'.format( len(pre_off), pre_off)) for node_id in pre_off: await offline_mbr_node(client, node_id) for node_id in [x for x in off_q if x in pre_off]: off_q.remove(node_id) logger.debug('{} nodes in offline queue: {}'.format( len(off_q), list(off_q))) # get ID and status details of ctlr node await client.get_data('status') ctlr_id = handle_node_status(client.data, cache) # update ctlr state tries await update_state_tries(client, ct.net_trie, ct.id_trie) logger.debug('net_trie has keys: {}'.format(list(ct.net_trie))) # for key in list(ct.net_trie): # logger.debug('net key {} has paylod: {}'.format(key, ct.net_trie[key])) # for key in list(ct.id_trie): # logger.debug('id key {} has payload: {}'.format(key, ct.id_trie[key])) logger.debug('id_trie has keys: {}'.format(list(ct.id_trie))) # handle node queues and publish messages logger.debug('{} nodes in node queue: {}'.format( len(node_q), list(node_q))) if len(node_q) > 0: handle_node_queues(node_q, staging_q) logger.debug('{} nodes in node queue: {}'.format( len(node_q), list(node_q))) logger.debug('{} nodes in staging queue: {}'.format( len(staging_q), list(staging_q))) for mbr_id in [x for x in staging_q if x not in list(ct.id_trie)]: if is_exit_node(mbr_id): await bootstrap_mbr_node(client, ctlr_id, mbr_id, netobj_q, ex=True) else: await bootstrap_mbr_node(client, ctlr_id, mbr_id, netobj_q) st.wait_cache.set(mbr_id, True, 90) publish_cfg_msg(ct.id_trie, mbr_id, addr='127.0.0.1') for mbr_id in [x for x in staging_q if x in list(ct.id_trie)]: publish_cfg_msg(ct.id_trie, mbr_id, addr='127.0.0.1') staging_q.remove(mbr_id) logger.debug('{} nodes in staging queue: {}'.format( len(staging_q), list(staging_q))) # refresh ctlr state tries again await update_state_tries(client, ct.net_trie, ct.id_trie) node_list = get_active_nodes(ct.id_trie) logger.debug('{} nodes in node_list: {}'.format( len(node_list), node_list)) if len(node_list) > 0: boot_list = get_bootstrap_list(ct.net_trie, ct.id_trie) logger.debug('{} nodes in boot_list: {}'.format( len(boot_list), boot_list)) if len(boot_list) != 0: await close_mbr_net(client, node_list, boot_list, min_nodes=3) else: await unwrap_mbr_net(client, node_list, boot_list, min_nodes=3) except Exception as exc: logger.error('netstate exception was: {}'.format(exc)) await cleanup_orphans(client) if list(ct.net_trie) == [] and list(ct.id_trie) != []: ct.id_trie.clear() raise exc