revert prev. commit + wip fix
This commit is contained in:
@ -63,18 +63,19 @@ class Display:
|
||||
self.term = serial.Serial(dev, baudrate=115200, timeout=timeout)
|
||||
self.storage = bytearray()
|
||||
if daemon:
|
||||
text = None
|
||||
todo_file = STARTUP_TEXT + '.todo'
|
||||
try:
|
||||
with open(STARTUP_TEXT) as f:
|
||||
with open(todo_file) as f:
|
||||
text = f.read()
|
||||
print('new startup text:')
|
||||
print(text)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
text = None
|
||||
if text:
|
||||
self.reset()
|
||||
self.show(*todo_text.split('\n')[0:3])
|
||||
self.show(*text.split('\n')[0:3])
|
||||
self.set_startup()
|
||||
os.rename(todo_file, STARTUP_TEXT)
|
||||
else:
|
||||
self.storage = None
|
||||
self.gethost(False)
|
||||
|
872
install.py
872
install.py
@ -61,16 +61,277 @@ max-lease-time 7200;
|
||||
authoritative;
|
||||
"""
|
||||
|
||||
SERVICES = dict(router=router, frappy=frappy, display=display)
|
||||
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 <if name> of <ethernet address>
|
||||
dhcp_server_cfg = [] # configuration for dhcp
|
||||
main_info = { # info to be determined depending on cfg
|
||||
'ifname': '', # name of interface of wan (dhcp) port
|
||||
'addr': '', # addr of wan port
|
||||
'current': None,
|
||||
'hostname': gethostname() # effective or given host name
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
sorted_if = sorted(net_addr)
|
||||
apuid = int(''.join(net_addr[sorted_if[0]].split(':')[-3:]), 16) & 0xfffffc
|
||||
|
||||
|
||||
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):
|
||||
text = '\n'.join(cfg.get(key, '') for key in ('line0', 'line1', 'line2'))
|
||||
text = text.replace('HOST', main_info['hostname']) \
|
||||
.replace('ADDR', main_info['addr']) + '\n'
|
||||
try:
|
||||
with open(STARTUP_TEXT) as f:
|
||||
if f.read() == text:
|
||||
return
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
print('new startup text:')
|
||||
print(text)
|
||||
with open(STARTUP_TEXT + '.todo', 'w') as f:
|
||||
f.write(text)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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 fname in fil:
|
||||
fname = fname.strip()
|
||||
if fname and exists(join(syspath, fname)):
|
||||
if exists(join(dirpath, fname)):
|
||||
print('ERROR: %s in %s, but also in repo -> ignored' % (fname, DEL))
|
||||
else:
|
||||
to_delete.append(fname)
|
||||
action.delete(to_delete)
|
||||
if missing:
|
||||
action.missing(missing)
|
||||
|
||||
|
||||
class Walker:
|
||||
def __init__(self):
|
||||
@ -128,435 +389,192 @@ class Do(Walker):
|
||||
os.remove(join(self.syspath, file))
|
||||
|
||||
|
||||
class Install:
|
||||
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
|
||||
"""
|
||||
|
||||
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(template)
|
||||
elif len(cfgfiles) > 1:
|
||||
print('ERROR: ambiguous cfg files: %s' % ', '.join(cfgfiles))
|
||||
else:
|
||||
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:
|
||||
print('> %s' % command)
|
||||
|
||||
def write_when_new(self, 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 f:
|
||||
old = f.read()
|
||||
except FileNotFoundError:
|
||||
old = None
|
||||
if old == content:
|
||||
def handle_config():
|
||||
cfgfile = None
|
||||
cfgfiles = []
|
||||
for file in glob(CFGPATH % ('*', apuid)):
|
||||
cfgfiles.append(file)
|
||||
for i in [1, 2, 3]:
|
||||
bad = glob(CFGPATH % ('*', apuid+i))
|
||||
if bad:
|
||||
print('cfg files found with bad apu id (use net addr of leftmost plug)')
|
||||
print(bad)
|
||||
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, []))
|
||||
newhostname = main['hostname']
|
||||
if not cfgfiles:
|
||||
# determine if display is present
|
||||
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'
|
||||
main['mainif'] = sorted_if[0]
|
||||
typ = 'controlbox'
|
||||
template = CONTROLBOX_TEMPLATE
|
||||
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
|
||||
# rightmost interface
|
||||
main['mainif'] = 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]
|
||||
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:
|
||||
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
|
||||
|
||||
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)
|
||||
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)
|
||||
show.dirty = True
|
||||
if enabled:
|
||||
unix_cmd('systemctl disable %s' % service)
|
||||
show.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):
|
||||
show.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():
|
||||
show.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('')
|
||||
return '\n'.join(result)
|
||||
|
||||
|
||||
print('---')
|
||||
to_install = Install(False)
|
||||
|
||||
if not result:
|
||||
print('fix first above errors')
|
||||
elif to_system.dirty and more_info == 'NONE':
|
||||
more_info = 'NONE'
|
||||
while True:
|
||||
doit = False
|
||||
print('---')
|
||||
answer = input('do above? ')
|
||||
if answer.lower().startswith('y'):
|
||||
Install(True)
|
||||
walk(Do())
|
||||
with open('/root/aputools/current', 'w') as fil:
|
||||
fil.write(to_install.current)
|
||||
else:
|
||||
print('nothing to do')
|
||||
show = Show()
|
||||
walk(show)
|
||||
result = handle_config()
|
||||
|
||||
if not result:
|
||||
print('fix first above errors')
|
||||
break
|
||||
if show.dirty and more_info == 'NONE':
|
||||
print('---')
|
||||
answer = input('enter y(es) to do above or a filename to check changes? ')
|
||||
doit = True
|
||||
if answer.lower() in {'y', 'yes'}:
|
||||
handle_config()
|
||||
walk(Do())
|
||||
with open('/root/aputools/current', 'w') as f:
|
||||
f.write(result)
|
||||
break
|
||||
if not answer:
|
||||
print('cancel')
|
||||
break
|
||||
else:
|
||||
print('nothing to do')
|
||||
break
|
||||
|
Reference in New Issue
Block a user