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)
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
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