diff --git a/aif.xml b/aif.xml index b27bcb1..ddc0ae7 100644 --- a/aif.xml +++ b/aif.xml @@ -6,7 +6,7 @@ - + diff --git a/aifclient.py b/aifclient.py index f8186c8..ee2e504 100755 --- a/aifclient.py +++ b/aifclient.py @@ -24,13 +24,14 @@ import socket import subprocess import ipaddress import copy -import pprint import urllib.request as urlrequest import urllib.parse as urlparse import urllib.response as urlresponse from ftplib import FTP_TLS from io import StringIO +logfile = '/root/log' + class aif(object): def __init__(self): @@ -191,7 +192,7 @@ class aif(object): for i in xmlobj.findall('storage/mount'): device = i.attrib['source'] mntpt = i.attrib['target'] - order = i.attrib['order'] + order = int(i.attrib['order']) if 'fstype' in i.keys(): fstype = i.attrib['fstype'] else: @@ -396,9 +397,9 @@ class archInstall(object): diskfmt = 'msdos' cmds.append(['sgdisk', '-om', d]) cmds.append(['parted', d, '--script', '-a', 'optimal']) - with open(os.devnull, 'w') as DEVNULL: + with open(logfile, 'a') as log: for c in cmds: - subprocess.call(c, stdout = DEVNULL, stderr = subprocess.STDOUT) + subprocess.call(c, stdout = log, stderr = subprocess.STDOUT) cmds = [] disksize = {} disksize['start'] = subprocess.check_output(['sgdisk', '-F', d]) @@ -440,10 +441,25 @@ class archInstall(object): mkformat[x] = d + str(p) cmds.append(mkformat) # TODO: add non-gpt stuff here? - with open(os.devnull, 'w') as DEVNULL: + with open(logfile, 'a') as log: for p in cmds: - subprocess.call(p, stdout = DEVNULL, stderr = subprocess.STDOUT) - usermntidx = self.mount.keys() + subprocess.call(p, stdout = log, stderr = subprocess.STDOUT) + usermntidx = list(self.mount.keys()) + usermntidx.sort() # We want to make sure we do this in order. + for k in usermntidx: + if self.mount[k]['mountpt'] == 'swap': + subprocess.call(['swapon', '-e', self.mount[k]['device']], stdout = log, stderr = subprocess.STDOUT) + else: + os.makedirs(self.mount[k]['mountpt'], exist_ok = True) + os.chown(self.mount[k]['mountpt'], 0, 0) + cmd = ['mount'] + if self.mount[k]['fstype']: + cmd.extend(['-t', self.mount[k]['fstype']]) + if self.mount[k]['opts']: + cmd.extend(['-o', self.mount[k]['opts']]) + cmd.extend([self.mount[k]['device'], self.mount[k]['mountpt']]) + subprocess.call(cmd, stdout = log, stderr = subprocess.STDOUT) + return() def mounts(self): mntorder = list(self.mount.keys()) @@ -507,35 +523,54 @@ class archInstall(object): cmounts['tmp'] = ['/bin/mount', '-t', 'tmpfs', '-o', 'mode=1777,strictatime,nodev,nosuid', 'tmp', chrootdir + '/tmp'] # Because the order of these mountpoints is so ridiculously important, we hardcode it. # Yeah, python 3.6 has ordered dicts, but do we really want to risk it? - with open(os.devnull, 'w') as DEVNULL: - for m in ('chroot', 'resolv', 'proc', 'sys', 'efi', 'dev', 'pts', 'shm', 'run', 'tmp'): - if cmounts[m]: - subprocess.call(cmounts[m], stdout = DEVNULL, stderr = subprocess.STDOUT) # Okay. So we finally have all the mounts bound. Whew. - return() + return(cmounts) - def setup(self): + def setup(self, mounts = False): # TODO: could we leverage https://github.com/hartwork/image-bootstrap somehow? I want to keep this close # to standard Python libs, though, to reduce dependency requirements. hostscript = [] chrootcmds = [] locales = [] locale = [] + if not mounts: + mounts = self.mounts() # Get the necessary fstab additions for the guest chrootfstab = subprocess.check_output(['genfstab', '-U', self.system['chrootpath']]) # Set up the time, and then kickstart the guest install. hostscript.append(['timedatectl', 'set-ntp', 'true']) + # Also start haveged if we have it. + try: + with open(os.devnull, 'w') as devnull: + subprocess.call(['haveged'], stderr = devnull) + except: + pass + # Make sure we get the keys, in case we're running from a minimal live env. + hostscript.append(['pacman-key', '--init']) + hostscript.append(['pacman-key', '--populate']) hostscript.append(['pacstrap', self.system['chrootpath'], 'base']) + # Run the basic host prep + #with open(os.devnull, 'w') as DEVNULL: + with open(logfile, 'a') as log: + for c in hostscript: + subprocess.call(c, stdout = log, stderr = subprocess.STDOUT) with open('{0}/etc/fstab'.format(self.system['chrootpath']), 'a') as f: f.write('# Generated by AIF-NG.\n') f.write(chrootfstab.decode('utf-8')) + with open(logfile, 'a') as log: + for m in ('resolv', 'proc', 'sys', 'efi', 'dev', 'pts', 'shm', 'run', 'tmp'): + if mounts[m]: + subprocess.call(mounts[m], stdout = log, stderr = subprocess.STDOUT) + # Validating this would be better with pytz, but it's not stdlib. dateutil would also work, but same problem. # https://stackoverflow.com/questions/15453917/get-all-available-timezones tzlist = subprocess.check_output(['timedatectl', 'list-timezones']).decode('utf-8').splitlines() if self.system['timezone'] not in tzlist: print('WARNING (non-fatal): {0} does not seem to be a valid timezone, but we\'re continuing anyways.'.format(self.system['timezone'])) - os.symlink('/usr/share/zoneinfo/{0}'.format(self.system['timezone']), - '{0}/etc/localtime'.format(self.system['chrootpath'])) + tzfile = '{0}/etc/localtime'.format(self.system['chrootpath']) + if os.path.lexists(tzfile): + os.remove(tzfile) + os.symlink('/usr/share/zoneinfo/{0}'.format(self.system['timezone']), tzfile) # This is an ugly hack. TODO: find a better way of determining if the host is set to UTC in the RTC. maybe the datetime module can do it. utccheck = subprocess.check_output(['timedatectl', 'status']).decode('utf-8').splitlines() utccheck = [x.strip(' ') for x in utccheck] @@ -564,7 +599,7 @@ class archInstall(object): with open('{0}/etc/locale.gen'.format(self.system['chrootpath']), 'w') as f: f.write('# Modified by AIF-NG.\n') f.write(''.join(localeraw)) - with open('{0}/etc/locale.conf', 'a') as f: + with open('{0}/etc/locale.conf'.format(self.system['chrootpath']), 'a') as f: f.write('# Added by AIF-NG.\n') f.write('LANG={0}\n'.format(locale[0].split()[0])) chrootcmds.append(['locale-gen']) @@ -703,10 +738,6 @@ class archInstall(object): f.write('# Generated by AIF-NG.\nDefaults:{0} !lecture\n{0} ALL=(ALL) ALL\n'.format(user)) # Base configuration- initcpio, etc. chrootcmds.append(['mkinitcpio', '-p', 'linux']) - # Run the basic host prep - with open(os.devnull, 'w') as DEVNULL: - for c in hostscript: - subprocess.call(c, stdout = DEVNULL, stderr = subprocess.STDOUT) return(chrootcmds) def bootloader(self): @@ -768,10 +799,10 @@ class archInstall(object): os.chown(filepath, 0, 0) # shouldn't be necessary, but just in case the umask's messed up or something. if t == 'pre': # We want to run these right away. - with open(os.devnull, 'w') as DEVNULL: + with open(logfile, 'a') as log: for i, s in enumerate(self.scripts['pre']): subprocess.call('/root/scripts/pre/{0}'.format(i), - stdout = DEVNULL, + stdout = log, stderr = subprocess.STDOUT) return() @@ -793,15 +824,15 @@ class archInstall(object): real_root = os.open("/", os.O_RDONLY) os.chroot(self.system['chrootpath']) # Does this even work with an os.chroot()? Let's hope so! - with open(os.devnull, 'w') as DEVNULL: + with open(logfile, 'a') as log: for c in chrootcmds: - subprocess.call(c, stdout = DEVNULL, stderr = subprocess.STDOUT) + subprocess.call(c, stdout = log, stderr = subprocess.STDOUT) for b in bootcmds: - subprocess.call(b, stdout = DEVNULL, stderr = subprocess.STDOUT) + subprocess.call(b, stdout = log, stderr = subprocess.STDOUT) if scriptcmds['post']: for i, s in enumerate(scriptcmds['post']): subprocess.call('/root/scripts/post/{0}'.format(i), - stdout = DEVNULL, + stdout = log, stderr = subproces.STDOUT) #os.system('{0}/root/aif-pre.sh'.format(self.system['chrootpath'])) #os.system('{0}/root/aif-post.sh'.format(self.system['chrootpath'])) @@ -812,14 +843,13 @@ class archInstall(object): os.symlink('../lib/systemd/systemd', '{0}/sbin/init'.format(chrootdir)) def unmount(self): - with open(os.devnull, 'w') as DEVNULL: - subprocess.call(['unmount', '-lR', self.system['chrootpath']], stdout = DEVNULL, stderr = subprocess.STDOUT) + with open(logfile, 'a') as log: + subprocess.call(['unmount', '-lR', self.system['chrootpath']], stdout = log, stderr = subprocess.STDOUT) def runInstall(confdict): install = archInstall(confdict) install.scriptcmds('pre') install.format() - install.mounts() install.chroot() install.scriptcmds('post') install.unmount() @@ -831,7 +861,8 @@ def main(): instconf = conf.buildDict() if 'DEBUG' in os.environ.keys(): import pprint - pprint.pprint(instconf) + with open(logfile, 'a') as log: + pprint.pprint(instconf, stream = log) runInstall(instconf) if __name__ == "__main__":