diff --git a/install.py b/install.py index e74e0f9..eed8d8d 100755 --- a/install.py +++ b/install.py @@ -61,277 +61,24 @@ max-lease-time 7200; authoritative; """ -ROUTER_TEMPLATE = """[Unit] -Description = Routing to locally connected hardware -After = network.target - -[Service] -ExecStart = /usr/bin/python3 /root/aputools/router.py - -[Install] -WantedBy = multi-user.target -""" - -FRAPPY_TEMPLATE = """[Unit] -Description = Running frappy server -After = network.target - -[Service] -User = l_samenv -ExecStart = /usr/bin/python3 /home/l_samenv/frappy/bin/secop-server %s - -[Install] -WantedBy = multi-user.target -""" - -DISPLAY_TEMPLATE = """[Unit] -Description = status display -After = network.target - -[Service] -User = root -ExecStart = /usr/bin/python3 /root/aputools/display.py -d - -[Install] -WantedBy = multi-user.target -""" - -pip_requirements = { - 'l_samenv': {}, - 'root': {} -} - - -net_addr = {} # dict of -main_info = {} # info about WAN interface -dhcp_server_cfg = [] # configuration for dhcp - - -def frappy(cfg=None, port=None, requirements='', **kwds): - if not cfg: - return None - req = pip_requirements['l_samenv'] - req[cfg] = '\n'.join(requirements.split(',')) - if port: - cfg = '-p %s %s' % (port, cfg) - with open('/home/l_samenv/frappy/requirements.txt') as f: - req['frappy main'] = f.read() - return FRAPPY_TEMPLATE % cfg - - -def router(**opts): - if not opts: - return None - try: - with open('/root/aputools/requirements.txt') as f: - pip_requirements['root']['aputools'] = f.read() - except FileNotFoundError: - pass - return ROUTER_TEMPLATE - - -def display_update(cfg): - dirty = False - lines = [] - for key in ['line0', 'line1', 'line2']: - value = cfg.get(key, '') - if value.startswith('HOST'): - line = main_info.get('hostname', '') - value = 'HOST: ' + line - elif value.startswith('ADDR'): - line = main_info.get('addr', '') - value = 'ADDR: ' + line - else: - line = value - lines.append(line) - if cfg.get(key) != value: - if doit: - cfg[key] = value - dirty = True - if dirty: - text = '\n'.join(lines) - if doit: - with open(STARTUP_TEXT, 'w') as f: - f.write(text) - else: - print('new startup text:') - print('\n'.join(lines)) - - -def display(**opts): - if not opts: - return None - return DISPLAY_TEMPLATE - - -def pip(): - for user, requirements in pip_requirements.items(): - if user == 'root': - tmpname = join('/root', 'pip_requirements.tmp') - pipcmd = 'pip3 install -r %s' % tmpname - else: - tmpname = join('/home', user, 'pip_requirements.tmp') - pipcmd = 'sudo --user %s pip3 install --user -r %s' % (user, tmpname) - filename = tmpname.replace('.tmp', '.txt') - content = ''.join('# --- for %s ---\n%s\n' % kv for kv in requirements.items()) - if write_when_new(filename, content, True): - if doit: - os.rename(filename, tmpname) - if os.system(pipcmd) == 0: - os.rename(tmpname, filename) - else: - os.remove(tmpname) - else: - print(pipcmd) - # unix_cmd(pipcmd, stdout=None) - show.dirty = True - - SERVICES = dict(router=router, frappy=frappy, display=display) -def unix_cmd(command, always=False, stdout=PIPE): - if doit or always: - if not always: - print('$ %s' % command) - result = Popen(command.split(), stdout=stdout).communicate()[0] - return (result or b'').decode() - else: - print('> %s' % command) +net_addr = {} # dict of +for netif in os.scandir('/sys/class/net'): + if netif.name != 'lo': # do not consider loopback interface + with open(os.path.join(netif.path, 'address')) as f: + addr = f.read().strip().lower() + net_addr[netif.name] = addr -def write_when_new(filename, content, ignore_reduction=False): - if content is None: - lines = [] - else: - if not content.endswith('\n'): - content += '\n' - lines = content.split('\n') - try: - with open(filename) as fil: - old = fil.read() - except FileNotFoundError: - old = None - if old == content: - return False - if ignore_reduction: - content_set = set(v for v in lines if not v.startswith('#')) - old_set = set(v for v in (old or '').split('\n') if not v.startswith('#')) - content_set -= old_set - if content_set: - print('missing packages: %s' % (', '.join(content_set - old_set))) - else: - return False - if doit: - if lines: - with open(filename, 'w') as fil: - fil.write(content) - else: - os.remove(filename) - elif filename.endswith(more_info): - print('changes in', filename) - old = [] if old is None else old.split('\n') - top_lines = 0 # in case of empty loop - for top_lines, (ol, ll) in enumerate(zip(old, lines)): - if ol != ll: - break - bottom_lines = 0 - for bottom_lines, (ol, ll) in enumerate(zip(reversed(old[top_lines:]), reversed(lines[top_lines:]))): - if ol != ll: - break - if bottom_lines == 0: - old.append('') - lines.append('') - bottom_lines += 1 - print("===") - print('\n'.join(old[:top_lines])) - print('<<<') - print('\n'.join(old[top_lines:-bottom_lines])) - print('---') - print('\n'.join(lines[top_lines:-bottom_lines])) - print('>>>') - print('\n'.join(old[-bottom_lines:-1])) - print("===") - return content +class Walker: + def __init__(self): + self.dirpath = None + self.syspath = None -def create_if(name, cfg): - result = dict( - TYPE='Ethernet', - NAME=name, - DEVICE=name, - BOOTPROTO='none', - ONBOOT='yes', - PROXY_METHOD='none', - BROWSER_ONLY='no', - IPV4_FAILURE_FATAL='yes', - IPV6INIT='no') - if cfg == 'off': - result['ONBOOT'] = 'no' - elif cfg.startswith('wan') or cfg == 'dhcp': - if main_info.get('mainif', name) != name: - raise ValueError('can not have more than one WAN/DHCP port') - main_info['mainif'] = name - main_info['addr'] = net_addr[name] - result['BOOTPROTO'] = 'dhcp' - # default: all <= 192.0.0.0 - # others have to be added explicitly - dhcp_server_cfg.append(('0.0.0.0/128.0.0.0', [])) - dhcp_server_cfg.append(('128.0.0.0/192.0.0.0', [])) - for nw in cfg.split(',')[1:]: - nw = IPv4Interface(nw).network - dhcp_server_cfg.append((nw.with_netmask, [])) - else: - cfgip = IPv4Interface(cfg) - network = cfgip.network - if network.prefixlen == 32: # or no prefix specified - otherip = IPv4Interface('%s/24' % cfgip.ip) - network = otherip.network - if str(cfgip.ip).endswith('.1'): - cfgip = network.network_address + 2 - else: - cfgip = network.network_address + 1 - dhcp_server_cfg.append((network.with_netmask, [(str(otherip.ip), str(otherip.ip))])) - result['IPADDR'] = str(cfgip) - else: # subnet with multiple adresses -> static adresses only. dhcp range not yet implemented - result['IPADDR'] = str(cfgip.ip) - result['NETMASK'] = network.netmask - result['PREFIX'] = str(network.prefixlen) - return result - - -def walk(action): - for dirpath, _, files in os.walk('.'): - syspath = dirpath[1:] # remove leading '.' - action.dirpath = dirpath - action.syspath = syspath - if files: - match, mismatch, missing = filecmp.cmpfiles(dirpath, syspath, files) - if mismatch: - newer = [f for f in mismatch if getmtime(join(syspath, f)) > getmtime(join(dirpath, f))] - if newer: - action.newer(newer) - if len(newer) < len(mismatch): - newer = set(newer) - action.older([f for f in mismatch if f not in newer]) - if missing: - if DEL in missing: - missing.remove(DEL) - with open(join(dirpath, DEL)) as fil: - to_delete = [] - for f in fil: - f = f.strip() - if f and exists(join(syspath, f)): - if exists(join(dirpath, f)): - print('ERROR: %s in %s, but also in repo -> ignored' % (f, DEL)) - else: - to_delete.append(f) - action.delete(to_delete) - if missing: - action.missing(missing) - - -class Show: +class Show(Walker): dirty = False def diff(self, title, files): @@ -364,7 +111,7 @@ class Show: self.show('remove from', self.syspath, files) -class Do: +class Do(Walker): def newer(self, files): for file in files: shutil.copy(join(self.syspath, file), join(self.dirpath, file)) @@ -381,191 +128,435 @@ class Do: os.remove(join(self.syspath, file)) -for netif in os.scandir('/sys/class/net'): - if netif.name != 'lo': # do not consider loopback interface - with open(os.path.join(netif.path, 'address')) as f: - addr = f.read().strip().lower() - net_addr[netif.name] = addr +class Install: + ROUTER_TEMPLATE = """[Unit] + Description = Routing to locally connected hardware + After = network.target -sorted_if = sorted(net_addr) + [Service] + ExecStart = /usr/bin/python3 /root/aputools/router.py -apuid = int(''.join(net_addr[sorted_if[0]].split(':')[-3:]), 16) & 0xffffff + [Install] + WantedBy = multi-user.target + """ + FRAPPY_TEMPLATE = """[Unit] + Description = Running frappy server + After = network.target -def handle_config(): - cfgfile = None - cfgfiles = [] - for file in glob(CFGPATH % ('*', apuid)): - cfgfiles.append(file) - with open('/etc/hostname') as f: - hostname = f.read().strip() - newhostname = hostname - if not cfgfiles: - disp = serial.Serial('/dev/ttyS1', baudrate=115200, timeout=1) - disp.write(b'\x1b\x1b\x01\xf3') - display_available = disp.read(8)[0:4] == b'\x1b\x1b\x05\xf3' - if display_available: - # leftmost interface labelled 'eth0' - wan_if = sorted_if[0] - typ = 'controlbox' - template = CONTROLBOX_TEMPLATE - else: - # rightmost interface - wan_id = sorted_if[-1] - typ = 'bare apu' - template = CONFIG_TEMPLATE - print('no cfg file found for this', typ, f'with id {apuid:%6.6x} (hostname={hostname})') - newhostname = input('enter host name: ') - if not newhostname: - print('no hostname given') - return False - cfgfile = CFGPATH % (newhostname, apuid) - with open(cfgfile, 'w') as f: - f.write(template) - elif len(cfgfiles) > 1: - print('ERROR: ambiguous cfg files: %s' % ', '.join(cfgfiles)) - else: - cfgfile = cfgfiles[0] - wan_if = None - if cfgfile != CFGPATH % (hostname, apuid): - if cfgfile: - newhostname = basename(cfgfile).rpartition('_')[0] - if doit: - os.system('sh sethostname.sh') - else: - if cfgfile: - print('replace host name %r by %r' % (hostname, newhostname)) - show.dirty = True - main_info['hostname'] = newhostname - if cfgfile is None: - return False - to_start = {} - ifname = '' - parser = ConfigParser() - try: - parser.read(cfgfile) - network = dict(parser['NETWORK']) - for ifname in net_addr: - content = create_if(ifname, network.pop(ifname, 'off')) - content = '\n'.join('%s=%s' % kv for kv in content.items()) - todo = write_when_new('/etc/sysconfig/network-scripts/ifcfg-%s' % ifname, content) - if todo and more_info == 'NONE': - print('change', ifname) - show.dirty = True - to_start[ifname] = 'if_restart' - if dhcp_server_cfg: - content = [DHCP_HEADER] - for subnet, rangelist in dhcp_server_cfg: - try: - adr, mask = subnet.split('/') - except Exception as e: - print(subnet, repr(e)) - continue - content.append('subnet %s netmask %s {\n' % (adr, mask)) - #content.append(' option netmask %s;\n' % mask) - for rng in rangelist: - content.append(' range %s %s;\n' % rng) - content.append('}\n') - content = ''.join(content) - todo = write_when_new('/etc/dhcp/dhcpd.conf', content) - if todo: - print('change dhcpd.conf') - to_start['dhcpd'] = 'restart' - show.dirty = True - elif doit: - unix_cmd('systemctl stop dhcpd') - unix_cmd('systemctl disable dhcpd') - content = [] - dirty = False - for section in parser.sections(): - if COMMENT not in parser[section]: - dirty = True - content.append('[%s]' % section) - content.append(COMMENT) - section_dict = dict(parser[section].items()) - if section == 'DISPLAY': - display_update(section_dict) - for key, value in section_dict.items(): - content.append('%s=%s' % (key, value)) - content.append('') - if dirty: + [Service] + User = l_samenv + ExecStart = /usr/bin/python3 /home/l_samenv/frappy/bin/secop-server %s + + [Install] + WantedBy = multi-user.target + """ + + DISPLAY_TEMPLATE = """[Unit] + Description = status display + After = network.target + + [Service] + User = root + ExecStart = /usr/bin/python3 /root/aputools/display.py -d + + [Install] + WantedBy = multi-user.target + """ + + main_if = '' + main_addr = '' + + def __init__(self, doit): + if not doit: + self.to_system = Show() + walk(self.to_system) + self.doit = doit + self.current = None + self.sorted_if = sorted(net_addr) + self.apuid = int(''.join(net_addr[sorted_if[0]].split(':')[-3:]), 16) & 0xfffffc + self.dhcp_server_cfg = [] # configuration for dhcp + self.pip_requirements = { + 'l_samenv': {}, + 'root': {} + } + cfgfile = None + cfgfiles = [] + for file in glob(CFGPATH % ('*', apuid)): + cfgfiles.append(file) + with open('/etc/hostname') as f: + hostname = f.read().strip() + self.hostname = hostname + if not cfgfiles: + disp = serial.Serial('/dev/ttyS1', baudrate=115200, timeout=1) + disp.write(b'\x1b\x1b\x01\xf3') + display_available = disp.read(8)[0:4] == b'\x1b\x1b\x05\xf3' + if display_available: + # leftmost interface labelled 'eth0' + typ = 'controlbox' + template = CONTROLBOX_TEMPLATE + else: + # rightmost interface + typ = 'bare apu' + template = CONFIG_TEMPLATE + print('no cfg file found for this', typ, f'with id {apuid:%6.6x} (hostname={hostname})') + self.hostname = input('enter host name: ') + if not self.hostname: + print('no hostname given') + return + cfgfile = CFGPATH % (self.hostname, apuid) with open(cfgfile, 'w') as f: - f.write('\n'.join(content)) - except Exception as e: - print('ERROR: can not handle %s %s: %r' % (hostname, ifname, e)) - raise - return False - - reload_systemd = False - for service, service_func in SERVICES.items(): - section = service.upper() - if parser.has_section(section): - servicecfg = service_func(**dict(parser[section])) + f.write(template) + elif len(cfgfiles) > 1: + print('ERROR: ambiguous cfg files: %s' % ', '.join(cfgfiles)) else: - servicecfg = None - result = unix_cmd('systemctl show -p WantedBy -p ActiveState %s' % service, True) - active = False - enabled = False - for line in result.split('\n'): - if line.startswith('WantedBy=m'): - enabled = True - elif line.strip() == 'ActiveState=active': - active = True - if servicecfg is None: - if active: - unix_cmd('systemctl stop %s' % service) - show.dirty = True - if enabled: - unix_cmd('systemctl disable %s' % service) - show.dirty = True + cfgfile = cfgfiles[0] + if cfgfile != CFGPATH % (hostname, apuid): + if cfgfile: + self.hostname = basename(cfgfile).rpartition('_')[0] + if doit: + os.system('sh sethostname.sh') + else: + if cfgfile: + print('replace host name %r by %r' % (hostname, self.hostname)) + self.to_system.dirty = True + if cfgfile is None: + raise ValueError('no config file') + to_start = {} + ifname = '' + parser = ConfigParser() + try: + parser.read(cfgfile) + network = dict(parser['NETWORK']) + for ifname in net_addr: + content = create_if(ifname, network.pop(ifname, 'off')) + content = '\n'.join('%s=%s' % kv for kv in content.items()) + todo = write_when_new('/etc/sysconfig/network-scripts/ifcfg-%s' % ifname, content) + if todo and more_info == 'NONE': + print('change', ifname) + self.to_system.dirty = True + to_start[ifname] = 'if_restart' + if self.dhcp_server_cfg: + content = [DHCP_HEADER] + for subnet, rangelist in self.dhcp_server_cfg: + try: + adr, mask = subnet.split('/') + except Exception as e: + print(subnet, repr(e)) + continue + content.append('subnet %s netmask %s {\n' % (adr, mask)) + # content.append(' option netmask %s;\n' % mask) + for rng in rangelist: + content.append(' range %s %s;\n' % rng) + content.append('}\n') + content = ''.join(content) + todo = write_when_new('/etc/dhcp/dhcpd.conf', content) + if todo: + print('change dhcpd.conf') + to_start['dhcpd'] = 'restart' + self.to_system.dirty = True + elif doit: + unix_cmd('systemctl stop dhcpd') + unix_cmd('systemctl disable dhcpd') + content = [] + dirty = False + for section in parser.sections(): + if COMMENT not in parser[section]: + dirty = True + content.append('[%s]' % section) + content.append(COMMENT) + section_dict = dict(parser[section].items()) + if section == 'DISPLAY': + display_update(section_dict) + for key, value in section_dict.items(): + content.append('%s=%s' % (key, value)) + content.append('') + if dirty: + with open(cfgfile, 'w') as f: + f.write('\n'.join(content)) + except Exception as e: + print('ERROR: can not handle %s %s: %r' % (hostname, ifname, e)) + raise + + reload_systemd = False + for service, service_func in SERVICES.items(): + section = service.upper() + if parser.has_section(section): + servicecfg = service_func(**dict(parser[section])) + else: + servicecfg = None + result = unix_cmd('systemctl show -p WantedBy -p ActiveState %s' % service, True) + active = False + enabled = False + for line in result.split('\n'): + if line.startswith('WantedBy=m'): + enabled = True + elif line.strip() == 'ActiveState=active': + active = True + if servicecfg is None: + if active: + unix_cmd('systemctl stop %s' % service) + self.to_system.dirty = True + if enabled: + unix_cmd('systemctl disable %s' % service) + self.to_system.dirty = True + else: + if not enabled: + to_start[service] = 'enable' + elif not active: + to_start[service] = 'restart' + if write_when_new('/etc/systemd/system/%s.service' % service, servicecfg): + self.to_system.dirty = True + reload_systemd = True + if servicecfg and to_start.get('service') is None: + to_start[service] = 'restart' + + pip() + if reload_systemd: + unix_cmd('systemctl daemon-reload') + for service, action in to_start.items(): + self.to_system.dirty = True + if action == 'if_restart': + unix_cmd('ifdown %s' % service) + unix_cmd('ifup %s' % service) + else: + if action == 'restart': + unix_cmd('systemctl restart %s' % service) + unix_cmd('systemctl enable %s' % service) + result = [f'config file:\n {cfgfile}'] + for section in parser.sections(): + result.append(section) + for key, value in parser.items(section): + result.append(f' {key} = {value}') + result.append('') + self.current = '\n'.join(result) + + def unix_cmd(self, command, always=False, stdout=PIPE): + if self.doit or always: + if not always: + print('$ %s' % command) + result = Popen(command.split(), stdout=stdout).communicate()[0] + return (result or b'').decode() else: - if not enabled: - to_start[service] = 'enable' - elif not active: - to_start[service] = 'restart' - if write_when_new('/etc/systemd/system/%s.service' % service, servicecfg): - show.dirty = True - reload_systemd = True - if servicecfg and to_start.get('service') is None: - to_start[service] = 'restart' + print('> %s' % command) - pip() - if reload_systemd: - unix_cmd('systemctl daemon-reload') - for service, action in to_start.items(): - show.dirty = True - if action == 'if_restart': - unix_cmd('ifdown %s' % service) - unix_cmd('ifup %s' % service) + def write_when_new(self, filename, content, ignore_reduction=False): + if content is None: + lines = [] else: - if action == 'restart': - unix_cmd('systemctl restart %s' % service) - unix_cmd('systemctl enable %s' % service) - result = [f'config file:\n {cfgfile}'] - for section in parser.sections(): - result.append(section) - for key, value in parser.items(section): - result.append(f' {key} = {value}') - result.append('') - return '\n'.join(result) + if not content.endswith('\n'): + content += '\n' + lines = content.split('\n') + try: + with open(filename) as f: + old = f.read() + except FileNotFoundError: + old = None + if old == content: + return False + if ignore_reduction: + content_set = set(v for v in lines if not v.startswith('#')) + old_set = set(v for v in (old or '').split('\n') if not v.startswith('#')) + content_set -= old_set + if content_set: + print('missing packages: %s' % (', '.join(content_set - old_set))) + else: + return False + if self.doit: + if lines: + with open(filename, 'w') as f: + f.write(content) + else: + os.remove(filename) + elif filename.endswith(more_info): + print('changes in', filename) + old = [] if old is None else old.split('\n') + top_lines = 0 # in case of empty loop + for top_lines, (ol, ll) in enumerate(zip(old, lines)): + if ol != ll: + break + bottom_lines = 0 + for bottom_lines, (ol, ll) in enumerate(zip(reversed(old[top_lines:]), reversed(lines[top_lines:]))): + if ol != ll: + break + if bottom_lines == 0: + old.append('') + lines.append('') + bottom_lines += 1 + print("===") + print('\n'.join(old[:top_lines])) + print('<<<') + print('\n'.join(old[top_lines:-bottom_lines])) + print('---') + print('\n'.join(lines[top_lines:-bottom_lines])) + print('>>>') + print('\n'.join(old[-bottom_lines:-1])) + print("===") + return content + + @staticmethod + def frappy(cfg=None, port=None, requirements='', **kwds): + if not cfg: + return None + req = pip_requirements['l_samenv'] + req[cfg] = '\n'.join(requirements.split(',')) + if port: + cfg = '-p %s %s' % (port, cfg) + with open('/home/l_samenv/frappy/requirements.txt') as f: + req['frappy main'] = f.read() + return FRAPPY_TEMPLATE % cfg + + def router(self, **opts): + if not opts: + return None + try: + with open('/root/aputools/requirements.txt') as f: + pip_requirements['root']['aputools'] = f.read() + except FileNotFoundError: + pass + return self.ROUTER_TEMPLATE + + def display_update(self, cfg): + dirty = False + lines = [] + for key in ['line0', 'line1', 'line2']: + value = cfg.get(key, '') + if value.startswith('HOST'): + line = self.hostname + value = 'HOST: ' + line + elif value.startswith('ADDR'): + line = self.main_addr + value = 'ADDR: ' + line + else: + line = value + lines.append(line) + if cfg.get(key) != value: + if self.doit: + cfg[key] = value + dirty = True + if dirty: + text = '\n'.join(lines) + if self.doit: + with open(STARTUP_TEXT, 'w') as f: + f.write(text) + else: + print('new startup text:') + print('\n'.join(lines)) + + def display(self, **opts): + if not opts: + return None + return self.DISPLAY_TEMPLATE + + def pip(self): + for user, requirements in pip_requirements.items(): + if user == 'root': + tmpname = join('/root', 'pip_requirements.tmp') + pipcmd = 'pip3 install -r %s' % tmpname + else: + tmpname = join('/home', user, 'pip_requirements.tmp') + pipcmd = 'sudo --user %s pip3 install --user -r %s' % (user, tmpname) + filename = tmpname.replace('.tmp', '.txt') + content = ''.join('# --- for %s ---\n%s\n' % kv for kv in requirements.items()) + if write_when_new(filename, content, True): + if self.doit: + os.rename(filename, tmpname) + if os.system(pipcmd) == 0: + os.rename(tmpname, filename) + else: + os.remove(tmpname) + else: + print(pipcmd) + # unix_cmd(pipcmd, stdout=None) + self.to_system.dirty = True + + def create_if(self, name, cfg): + result = dict( + TYPE='Ethernet', + NAME=name, + DEVICE=name, + BOOTPROTO='none', + ONBOOT='yes', + PROXY_METHOD='none', + BROWSER_ONLY='no', + IPV4_FAILURE_FATAL='yes', + IPV6INIT='no') + if cfg == 'off': + result['ONBOOT'] = 'no' + elif cfg.startswith('wan') or cfg == 'dhcp': + if name != self.main_if and self.main_if + raise ValueError('can not have more than one WAN/DHCP port') + self.main_if = name + self.main_addr = net_addr[name] + result['BOOTPROTO'] = 'dhcp' + # default: all <= 192.0.0.0 + # others have to be added explicitly + self.dhcp_server_cfg.append(('0.0.0.0/128.0.0.0', [])) + self.dhcp_server_cfg.append(('128.0.0.0/192.0.0.0', [])) + for nw in cfg.split(',')[1:]: + nw = IPv4Interface(nw).network + self.dhcp_server_cfg.append((nw.with_netmask, [])) + else: + cfgip = IPv4Interface(cfg) + network = cfgip.network + if network.prefixlen == 32: # or no prefix specified + otherip = IPv4Interface('%s/24' % cfgip.ip) + network = otherip.network + if str(cfgip.ip).endswith('.1'): + cfgip = network.network_address + 2 + else: + cfgip = network.network_address + 1 + self.dhcp_server_cfg.append((network.with_netmask, [(str(otherip.ip), str(otherip.ip))])) + result['IPADDR'] = str(cfgip) + else: # subnet with multiple adresses -> static adresses only. dhcp range not yet implemented + result['IPADDR'] = str(cfgip.ip) + result['NETMASK'] = network.netmask + result['PREFIX'] = str(network.prefixlen) + return result + + def walk(self, action): + for dirpath, _, files in os.walk('.'): + syspath = dirpath[1:] # remove leading '.' + action.dirpath = dirpath + action.syspath = syspath + if files: + match, mismatch, missing = filecmp.cmpfiles(dirpath, syspath, files) + if mismatch: + newer = [f for f in mismatch if getmtime(join(syspath, f)) > getmtime(join(dirpath, f))] + if newer: + action.newer(newer) + if len(newer) < len(mismatch): + newer = set(newer) + action.older([f for f in mismatch if f not in newer]) + if missing: + if DEL in missing: + missing.remove(DEL) + with open(join(dirpath, DEL)) as f: + to_delete = [] + for line in f: + line = line.strip() + if fline and exists(join(syspath, line)): + if exists(join(dirpath, line)): + print('ERROR: %s in %s, but also in repo -> ignored' % (line, DEL)) + else: + to_delete.append(f) + action.delete(to_delete) + if missing: + action.missing(missing) -doit = False print('---') -show = Show() -walk(show) -result = handle_config() +to_install = Install(False) if not result: print('fix first above errors') -elif show.dirty and more_info == 'NONE': +elif to_system.dirty and more_info == 'NONE': print('---') answer = input('do above? ') - doit = True if answer.lower().startswith('y'): - handle_config() + Install(True) walk(Do()) - with open('/root/aputools/current', 'w') as f: - f.write(result) + with open('/root/aputools/current', 'w') as fil: + fil.write(to_install.current) else: print('nothing to do')