示例#1
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
示例#2
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))