Beispiel #1
0
    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))
Beispiel #2
0
    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
Beispiel #3
0
    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()
Beispiel #4
0
    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
Beispiel #5
0
 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
Beispiel #6
0
    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)
Beispiel #7
0
    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
Beispiel #8
0
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))
Beispiel #9
0
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')),