Ejemplo n.º 1
0
    def configure_dhcp_files(self):
        for _dir in [
                os.path.dirname(ztp_opts_conf),
                os.path.dirname(ztp_hosts_conf)
        ]:
            if not os.path.isdir(_dir):
                os.mkdir(_dir)
                utils.set_perm(_dir, user='******', group='rwx', other='rx')

        # -- get dhcp-range= line from wired-dhcp.conf and override it in ztp.conf with shorter lease time for ztp
        with open('/etc/ConsolePi/dnsmasq.d/wired-dhcp/wired-dhcp.conf') as f:
            dhcp_main_lines = f.readlines()

        line = [
            f"{','.join(_line.split(',')[0:-1])},{ztp_lease_time}\n"
            for _line in dhcp_main_lines
            if _line.strip().startswith('dhcp-range=')
        ]
        if line and len(line) == 1:
            ztp_main_lines.insert(0, line[0])
        else:
            print(
                "!! Error occured getting dhcp-range from wired-dhcp.conf lease-time will not be updated for ZTP"
            )

        for _file, _lines in zip(
            [ztp_main_conf, ztp_hosts_conf, ztp_opts_conf],
            [self.ztp_main_lines, self.ztp_host_lines, self.ztp_opt_lines]):
            with open(_file, "w") as f:
                f.writelines(_lines)
Ejemplo n.º 2
0
    def update_local_cloud_file(self,
                                remote_consoles=None,
                                current_remotes=None,
                                local_cloud_file=None):
        """Update local cloud cache (cloud.json).

        Verifies the newly discovered data is more current than what we already know and updates the local cloud.json file if so
        The Menu uses cloud.json to populate remote menu items

        params:
            remote_consoles: The newly discovered data (from Gdrive or mdns)
            current_remotes: The current remote data fetched from the local cloud cache (cloud.json)
                - func will retrieve this if not provided
            local_cloud_file The path to the local cloud file (global var cloud.json)

        returns:
        dict: The resulting remote console dict representing the most recent data for each remote.
        """
        local_cloud_file = (config.static.get("LOCAL_CLOUD_FILE")
                            if local_cloud_file is None else local_cloud_file)

        if len(remote_consoles) > 0:
            if current_remotes is None:
                current_remotes = self.data = config.remote_update(
                )  # grabs the remote data from local cloud cache

        # update current_remotes dict with data passed to function
        if len(remote_consoles) > 0:
            if current_remotes is not None:
                for _ in current_remotes:
                    if _ not in remote_consoles:
                        if ("fail_cnt" not in current_remotes[_]
                                or current_remotes[_]["fail_cnt"] < 2):
                            remote_consoles[_] = current_remotes[_]
                        elif (remote_consoles.get(_)
                              and "fail_cnt" not in remote_consoles[_]
                              and "fail_cnt" in current_remotes[_]):
                            remote_consoles[_]["fail_cnt"] = current_remotes[
                                _]["fail_cnt"]
                    else:

                        # -- VERBOSE DEBUG --
                        log.debugv(
                            "[CACHE UPD] \n--{}-- \n    remote upd_time: {}\n    remote rem_ip: {}\n    remote source: {}\n    cache rem upd_time: {}\n    cache rem_ip: {}\n    cache source: {}\n"
                            .format(  # NoQA
                                _,
                                time.strftime(
                                    "%a %x %I:%M:%S %p %Z",
                                    time.localtime(
                                        remote_consoles[_]["upd_time"]),
                                ) if "upd_time" in remote_consoles[_] else
                                None,  # NoQA
                                remote_consoles[_]["rem_ip"]
                                if "rem_ip" in remote_consoles[_] else None,
                                remote_consoles[_]["source"]
                                if "source" in remote_consoles[_] else None,
                                time.strftime(
                                    "%a %x %I:%M:%S %p %Z",
                                    time.localtime(
                                        current_remotes[_]["upd_time"]),
                                ) if "upd_time" in current_remotes[_] else
                                None,  # NoQA
                                current_remotes[_]["rem_ip"]
                                if "rem_ip" in current_remotes[_] else None,
                                current_remotes[_]["source"]
                                if "source" in current_remotes[_] else None,
                            ))
                        # -- END VERBOSE DEBUG --

                        # No Change Detected (data passed to function matches cache)
                        if "last_ip" in current_remotes[_]:
                            del current_remotes[_]["last_ip"]
                        if remote_consoles[_] == current_remotes[_]:
                            log.debug(
                                "[CACHE UPD] {} No Change in info detected".
                                format(_))

                        # only factor in existing data if source is not mdns
                        elif ("upd_time" in remote_consoles[_]
                              or "upd_time" in current_remotes[_]):
                            if ("upd_time" in remote_consoles[_]
                                    and "upd_time" in current_remotes[_]):
                                if (current_remotes[_]["upd_time"] >
                                        remote_consoles[_]["upd_time"]):
                                    remote_consoles[_] = current_remotes[_]
                                    log.info(
                                        f"[CACHE UPD] {_} Keeping existing data from {current_remotes[_].get('source', '')} "
                                        "based on more current update time")
                                elif (remote_consoles[_]["upd_time"] >
                                      current_remotes[_]["upd_time"]):
                                    log.info(
                                        "[CACHE UPD] {} Updating data from {} "
                                        "based on more current update time".
                                        format(_,
                                               remote_consoles[_]["source"]))
                                else:  # -- Update Times are equal --
                                    if (current_remotes[_].get("adapters") and
                                            remote_consoles[_].get("adapters")
                                            and current_remotes[_]["adapters"].
                                            keys() != remote_consoles[_]
                                        ["adapters"].keys()
                                        ) or remote_consoles[_].get(
                                            "interfaces",
                                            {}) != current_remotes[_].get(
                                                "interfaces", {}):
                                        log.warning(
                                            "[CACHE UPD] {} current cache update time and {} update time are equal"
                                            " but data appears to have changed. Updating"
                                            .format(
                                                _,
                                                remote_consoles[_]["source"]))
                            elif "upd_time" in current_remotes[_]:
                                remote_consoles[_] = current_remotes[_]
                                log.info(
                                    "[CACHE UPD] {} Keeping existing data based *existence* of update time "
                                    "which is lacking in this update from {}".
                                    format(_, remote_consoles[_]["source"]))

            for _try in range(0, 2):
                try:
                    with open(local_cloud_file, "w") as cloud_file:
                        cloud_file.write(
                            json.dumps(remote_consoles,
                                       indent=4,
                                       sort_keys=True))
                        utils.set_perm(
                            local_cloud_file
                        )  # a hack to deal with perms ~ consolepi-details del func
                        break
                except PermissionError:
                    utils.set_perm(local_cloud_file)

        else:
            log.warning(
                "[CACHE UPD] cache update called with no data passed, doing nothing"
            )

        return remote_consoles
Ejemplo n.º 3
0
    def get_ser2net(self):
        '''Parse ser2net.conf to extract connection info for serial adapters

        retruns 2 level dict (empty dict if ser2net.conf not found or empty):
            {
                <adapter name or alias>: {
                    "baud": <baud>,
                    "dbits": <data bits>,
                    "flow": "<flow control>",
                    "parity": "<parity>",
                    "sbits": <stop bits>,
                    "port": <telnet port (ser2net),
                    "logfile": None or logfile if defined in ser2net.conf
                    "cmd": picocom command string used in menu
                    "line": The line from ser2net.conf
                }
            }
        '''
        ########################################################
        # --- ser2net (3.x) config lines look like this ---
        # ... 9600 NONE 1STOPBIT 8DATABITS XONXOFF LOCAL -RTSCTS
        # ... 9600 8DATABITS NONE 1STOPBIT banner
        ########################################################
        # utils = self.utils
        if not utils.valid_file(self.static.get('SER2NET_FILE')):
            log.warning(
                'No ser2net.conf file found unable to extract port definition',
                show=True)
            return {}

        ser2net_conf = {}
        trace_files = {}
        with open(self.static['SER2NET_FILE']) as cfg:
            for line in cfg:
                if 'TRACEFILE:' in line:
                    line = line.split(':')
                    trace_files[line[1]] = line[2]
                    continue
                elif not line[0].isdigit():
                    continue
                _line = line.strip('\n')
                line = line.split(':')
                tty_port = int(line[0])
                tty_dev = line[3]

                # Reset defaults
                # baud is used to determine parsing failure
                dbits = 8
                parity = 'n'
                flow = 'n'
                sbits = 1
                logfile = None
                log_ptr = None

                connect_params = line[4].replace(',', ' ').split()
                baud = None
                for option in connect_params:
                    if option in self.static.get('VALID_BAUD', [
                            '300', '1200', '2400', '4800', '9600', '19200',
                            '38400', '57600', '115200'
                    ]):
                        baud = int(option)
                    elif 'DATABITS' in option:
                        dbits = int(option.replace('DATABITS',
                                                   ''))  # int 5 - 8
                        if dbits < 5 or dbits > 8:
                            log.warning(
                                f'{tty_dev}: Invalid value for "data bits" found in ser2net.conf falling back to 8',
                                show=True)
                            dbits = 8
                    elif option in ['EVEN', 'ODD', 'NONE']:
                        parity = option[0].lower(
                        )  # converts to e o n used by picocom
                    elif option == 'XONXOFF':
                        flow = 'x'
                    elif option == 'RTSCTS':
                        flow = 'h'
                    elif 'STOPBIT' in option:  # Not used by picocom
                        sbits = int(option[0]) if option[0].isdigit else 1
                    elif 'tb=' in option or 'tr=' in option or 'tw=' in option:
                        log_ptr = option
                        logfile = option.split('=')[1]

                # Use baud to determine if options were parsed correctly
                if baud is None:
                    log.warning(
                        f'{tty_dev} found in ser2net but unable to parse baud falling back to {self.default_baud}',
                        show=True)
                    baud = self.default_baud

                # parse TRACEFILE defined in ser2net.conf
                cmd_base = f'picocom {tty_dev} --baud {baud} --flow {flow} --databits {dbits} --parity {parity}'
                if self.picocom_ver > 1:  # picocom ver 1.x in Stretch doesn't support "--stopbits"
                    cmd_base = cmd_base + f' --stopbits {sbits}'
                if logfile:
                    logfile = trace_files[logfile]
                    logfile = logfile.replace('\\p', str(tty_port)).replace(
                        '\\d',
                        tty_dev.split('/')[-1])
                    logfile = logfile.replace(
                        '\\s', f'{baud}_{dbits}{parity.upper()}{sbits}')
                    logfile = logfile.split(
                        '\\'
                    )[0] + '-{{timestamp}}.log'  # + time.strftime('%H.%M.log')
                    cmd = cmd_base + f' --logfile {logfile}'
                    utils.do_shell_cmd(
                        f"mkdir -p {'/'.join(logfile.split('/')[0:-1])}")
                    utils.set_perm('/'.join(logfile.split('/')[0:-1]))
                else:
                    cmd = cmd_base

                # update dict with values for this device
                ser2net_conf[tty_dev] = {
                    'port': tty_port,
                    'baud': baud,
                    'dbits': dbits,
                    'parity': parity,
                    'flow': flow,
                    'sbits': sbits,
                    'logfile': logfile,
                    'log_ptr': log_ptr,
                    'cmd': cmd,
                    'line': _line
                }

        return ser2net_conf