def get_node_by_name(self, node_name: str): """ Search a node instance by it's name. :param node_name: the node's name to search. :return: Node instance whose name is node_name. :raise TypeError: Node name given as parameter is None. :raise DataNotFoundError: Node instance has not been found. """ if node_name is None: raise TypeError("No node name has been given. node_name is None.") content_as_string = self.db.get(Node.KEY_PREFIX + node_name) if content_as_string is not None: content_as_dict = ast.literal_eval( content_as_string.decode('utf-8')) if type(content_as_dict) == dict: # node_type = self.get_type_by_name(content_as_dict.get('type', '')) n = Node() n.from_dict(node_dict=content_as_dict) # n.from_dict(node_dict=content_as_dict, node_type=node_type) return n else: raise TypeError( "Type obtained from redis {} after conversion isn't a dictionary." .format(Node.KEY_PREFIX + node_name)) raise DataNotFoundError( "Node whose name is {} hasn't been found in the db.".format( node_name))
def remove_node_from_sector(self, node: Node): """ Removes a node from a sector. :param node: Node instance to be removed. """ if node is None: return 0 count = 0 self.nodesListMutex.acquire() if self.db.exists(node.get_key()): self.db.delete(node.get_key()) self.db.delete(str(node.ip_address)) count = self.db.lrem(name=node.sector, value=node.get_key(), count=0) elif self.db.exists(str(node.ip_address)): # Remove defunct entries self.db.delete(str(node.ip_address)) count = self.db.lrem(name=node.sector, value=str(node.ip_address), count=0) self.nodesListMutex.release() return count
def __init__(self, path, interface='eth0'): """ Creates a new object instance. :param path: the configuration file's location """ # Creates the objects that wrap the host's settings. self.node = Node() self.logger = logging.getLogger('BBB') # Parameters that define absolute locations inside the host self.configuration_file_path = path # The interface used (ethernet port on the Beaglebone). self.interface_name = interface self.read_node_parameters() self.node.type = Type.from_code(Type.UNDEFINED) self.node.update_state(NodeState.CONNECTED) self.node.ip_address = str( ipaddress.ip_address(self.get_ip_address()[0])) self.current_config_json_mtime = None # Load the data from the cfg file. self.check_config_json()
def append_node(self, new_node: Node = None): """ Append a new node to the database, adding it to sector list accordingly. :param new_node: the node to be added. :return: True if the node was successfully added. False, otherwise. :raise DifferentSectorNodeError: the user tries to append a node that is already in another sector. :raise TypeError: node given as parameter is None or its name is not valid. """ if new_node is None or new_node.name is None or new_node.name == "": raise TypeError( "Node given as parameter is None or its name is not valid.") self.nodesListMutex.acquire() # Trying to add a node that exists in other sector try: old_node = self.get_node_by_name(new_node.name) # node already exists if old_node.sector != new_node.sector: # node was initially added on another sector. self.nodesListMutex.release() raise DifferentSectorNodeError( "Cannot append node to sector {} if it already belongs to {}" .format(new_node.sector, old_node.sector)) # Remove old node self.db.delete(old_node.get_key()) self.db.lrem(name=new_node.sector, value=old_node.get_key(), count=0) except DataNotFoundError: pass if not self.db.exists(new_node.get_key()): self.db.lpush(new_node.sector, new_node.get_key()) node_key, node_value = new_node.to_dict() self.db.set(node_key, node_value) success = self.db.set(str(new_node.ip_address), new_node.get_key()) self.nodesListMutex.release() return success
def get_ping_node(self, **kwargs): """ Get a ping node. Pass a node object. :param key: Ping key to look for. """ key = kwargs.get('key', None) node = None if key: self.pingNodesListMutex.acquire() # if exists ... self.db.sismember(PING_NODES, key) content_as_string = self.db.get(key) if content_as_string != None: res = ast.literal_eval(content_as_string.decode('utf-8')) node = Node() node.from_dict(res['n']) # node.from_dict(res['n'], res['t']) self.pingNodesListMutex.release() return node
def update_ping_hosts(self, **kwargs): """ Updates a given node's counter. :param node_dict: Node dictionary representation. """ node_dict = kwargs.get('node_dict', None) # type_dict = kwargs.get('type_dict', None) # if not node_dict: # return # t = Type() # t.from_dict(type_dict) node = Node() node.from_dict(node_dict) # node.from_dict(node_dict, t) # print(node, ' ACQUIRE') self.db.update_ping_node_list(node=node)
def append_node(self, new_node: Node = None): """ Append a new node into the database. :param new_node: the node to be appended. :return: True or False """ success_db = self.db.append_node(new_node) success_local = False if success_db: sector = new_node.sector self.updateNodesLockList[sector].acquire() new_node.counter = MAX_LOST_PING if new_node in self.nodes[sector]["configured"]: # Updates the current instance if the node is already in the db. index = self.nodes[sector]["configured"].index(new_node) self.nodes[sector]["configured"][index].name = new_node.name self.nodes[sector]["configured"][ index].ip_address = new_node.ip_address else: self.nodes[sector]["configured"].append(new_node) # Verify unregistered nodes and remove from the list if they are disconnected if new_node in self.nodes[sector]["unconfigured"]: index = self.nodes[sector]["unconfigured"].index(new_node) if new_node.is_strictly_equal( self.nodes[sector]["unconfigured"][index]): self.nodes[sector]["unconfigured"].remove(new_node) # Local lists updated successfully. success_local = True self.updateNodesLockList[sector].release() return success_db and success_local
class BBB: """ A class to represent a Beaglebone host. """ CONFIG_JSON_PATH = '/opt/device.json' def __init__(self, path, interface='eth0'): """ Creates a new object instance. :param path: the configuration file's location """ # Creates the objects that wrap the host's settings. self.node = Node() self.logger = logging.getLogger('BBB') # Parameters that define absolute locations inside the host self.configuration_file_path = path # The interface used (ethernet port on the Beaglebone). self.interface_name = interface self.read_node_parameters() self.node.type = Type.from_code(Type.UNDEFINED) self.node.update_state(NodeState.CONNECTED) self.node.ip_address = str( ipaddress.ip_address(self.get_ip_address()[0])) self.current_config_json_mtime = None # Load the data from the cfg file. self.check_config_json() def check_config_json(self): """ Verify if the version loaded of the file config.json is the lattest available. If not, load again. """ if os.path.exists(BBB.CONFIG_JSON_PATH): config_json_mtime = os.path.getmtime(BBB.CONFIG_JSON_PATH) if self.current_config_json_mtime == None or config_json_mtime != self.current_config_json_mtime: with open(BBB.CONFIG_JSON_PATH, 'r') as f: config = json.load(f) self.current_config_json_mtime = config_json_mtime self.node.type.code = int(config['device']) self.node.details = '{}\tbaudrate={}'.format( config['details'], config['baudrate']) self.node.config_time = config['time'] self.write_node_configuration() def get_current_config(self): """ Returns a dictionary containing the host's information and the command type. :return: message representing the current configuration. """ self.check_config_json() dict_res = self.node.to_dict() return {'comm': Command.PING, 'n': dict_res[1]} def reboot(self): """ Reboots this node. """ self.logger.info( "Setting state to reboot ... Waiting for the next ping ...") self.node.update_state(NodeState.REBOOTING) time.sleep(3.) self.logger.info("Rebooting system.") os.system('reboot') def update_hostname(self, new_hostname): """ Updates the host with anew hostname. """ old_hostname = self.node.name.replace(':', '--') new_hostname = new_hostname.replace(':', '--') if old_hostname != new_hostname: self.logger.info("Updating current hostname from {} to {}.".format( old_hostname, new_hostname)) with open("/etc/hostname", "w") as hostnameFile: hostnameFile.write(new_hostname) hostnameFile.close() os.system("hostname {}".format(new_hostname)) self.node.name = new_hostname def update_ip_address(self, dhcp_manual, new_ip_address="", new_mask="", new_gateway=""): """ Updates the host with a new ip address """ if self.node.ip_address != new_ip_address: if new_ip_address != "": self.logger.info( "Updating current ip address from {} to {}, mask {}, default gateway {}." .format(self.node.ip_address, new_ip_address, new_mask, new_gateway)) else: self.logger.info( "Updating current ip address from {} to DHCP.".format( self.node.ip_address)) self.change_ip_address(dhcp_manual, new_ip_address, new_mask, new_gateway) self.node.ip_address = self.get_ip_address()[0] def read_node_parameters(self): """ Reads current node parameters, editing the name to include ':' where needed. """ try: self.read_node_configuration() except IOError: self.logger.error( "Configuration file not found. Adopting default values.") name = subprocess.check_output(["hostname" ]).decode('utf-8').strip('\n') self.node.name = name.replace('--', ':') def read_node_configuration(self): """ Reads the current node configuration from a binary file with the pickle module. """ with open(self.configuration_file_path, 'rb') as file: self.node = pickle.load(file) file.close() self.logger.info("Node configuration file read successfully.") def write_node_configuration(self): """ Writes the current node configuration to a binary file with the pickle module. Overrides old files. """ with open(self.configuration_file_path, 'wb') as file: file.write(pickle.dumps(self.node)) file.close() self.logger.info("Node configuration file updated successfully.") def get_ip_address(self): """ Get the host's IP address with the 'ip addr' Linux command. :return: a tuple containing the host's ip address and network address. """ command_out = subprocess.check_output( "ip addr show dev {} scope global".format( self.interface_name).split()).decode('utf-8') lines = command_out.split('\n') address_line = lines[2].split()[1] return ipaddress.IPv4Address(address_line[0:address_line.index('/')]), \ ipaddress.IPv4Network(address_line, strict=False) def change_ip_address(self, dhcp_manual, new_ip_address="", new_mask="", new_gateway=""): """ Execute the connmanclt tool to change the host' IP address. :param dchp_manual: either if its a DHCP ("dhcp") ou STATIC IP ("manual") :param new_ip_address: the new IP address. An ipaddress.IPv4Address object. :param net_address: new sub-network address. An ipaddress.IPv4Network object. :param default_gateway_address: the new default gateway :raise TypeError: new_ip_address or net_address are None or are neither ipaddress nor string objects. """ service = self.get_connman_service_name() self.logger.debug("Service for interface {} is {}.".format( self.interface_name, service)) if new_ip_address != "": self.logger.info( 'Changing current IP address from {} to {}'.format( self.get_ip_address()[0], new_ip_address)) if new_gateway is None: new_gateway = Sector.get_default_gateway_of_address( new_ip_address) else: self.logger.info( 'Changing current IP address from {} to DHCP'.format( self.get_ip_address()[0])) subprocess.check_output([ 'connmanctl config {} --ipv4 {} {} {} {}'.format( service, dhcp_manual, new_ip_address, new_mask, new_gateway) ], shell=True) time.sleep(2) self.logger.debug('IP address after update is {}'.format( self.get_ip_address()[0])) def update_nameservers(self, nameserver_1="", nameserver_2=""): service = self.get_connman_service_name() self.logger.info('Changing DSN server to {} and {}'.format( nameserver_1, nameserver_2)) subprocess.check_output([ 'connmanctl config {} --nameservers {} {}'.format( service, nameserver_1, nameserver_2) ], shell=True) def get_connman_service_name(self): """ Returns the service name assigned to manage an interface. @fixme: services with spaces on their names won't be detected! :return: A service's name. :raise ValueError: service is not found. """ services = subprocess.check_output(['connmanctl services'], stderr=subprocess.STDOUT, shell=True).decode('utf-8') for service in services.split(): if service.startswith('ethernet_'): service_properties = subprocess.check_output( ['connmanctl services ' + service], stderr=subprocess.STDOUT, shell=True).decode('utf-8') for prop in service_properties.split('\n'): if prop.strip().startswith('Ethernet'): data = prop.split('[')[1].strip()[:-1].split(',') for d_info in data: d_info = d_info.strip() if d_info.startswith('Interface'): if d_info == 'Interface={}'.format( self.interface_name): return service raise ValueError( 'Connmanctl service could not be found for interface {}'.format( self.interface_name))
import traceback pingSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def get_message(node): node_key, node_info = node.to_dict() message = {'comm': Command.PING, 'n': node_info} chk = NetUtils.checksum(str(message)) payload = {'chk': chk, 'payload': message} pack = msgpack.packb(payload, use_bin_type=True) return pack while True: try: pingSocket.sendto(get_message(Node(name='1', ip_address='10.128.0.0')), ('0.0.0.0', 9876)) pingSocket.sendto( get_message(Node(name='2', ip_address='10.128.0.20')), ('0.0.0.0', 9876)) pingSocket.sendto( get_message(Node(name='3', ip_address='10.128.0.30')), ('0.0.0.0', 9876)) pingSocket.sendto( get_message(Node(name='4', ip_address='10.128.0.40')), ('0.0.0.0', 9876)) pingSocket.sendto( get_message(Node(name='5', ip_address='10.128.0.50')), ('0.0.0.0', 9876)) pingSocket.sendto( get_message(Node(name='6', ip_address='10.128.0.60')),