[c6cf378] | 1 | #!/usr/bin/python3 |
---|
| 2 | |
---|
| 3 | import sys |
---|
| 4 | import os |
---|
| 5 | import logging |
---|
[acc32a2] | 6 | import subprocess |
---|
| 7 | import glob |
---|
| 8 | import stat |
---|
[960b66e] | 9 | import shutil |
---|
[d4564f6] | 10 | import argparse |
---|
[fe39c2d] | 11 | import datetime |
---|
[c6cf378] | 12 | |
---|
[31e3607] | 13 | curdir = os.path.dirname (__file__) |
---|
| 14 | sys.path.insert (0, curdir) |
---|
[d1822ec] | 15 | from boottools import utils, apt, btog |
---|
[c6cf378] | 16 | |
---|
[53310a8] | 17 | os.chdir (curdir) |
---|
| 18 | |
---|
[5ecf9f3] | 19 | def _logging (lvl='INFO', logfile='/tmp/boot-tools_installation.log'): |
---|
[f04ace2] | 20 | numeric_level = getattr (logging, lvl.upper(), None) |
---|
| 21 | if numeric_level is None: |
---|
| 22 | numeric_level = getattr (logging, 'INFO') |
---|
| 23 | |
---|
[c5e9f68] | 24 | logging.basicConfig ( |
---|
| 25 | format='%(levelname)s %(asctime)s (%(funcName)s) %(message)s', |
---|
[0371870] | 26 | level=numeric_level, |
---|
[c5e9f68] | 27 | handlers = [ |
---|
[5ecf9f3] | 28 | logging.FileHandler (logfile), |
---|
[c5e9f68] | 29 | logging.StreamHandler (sys.stdout), |
---|
| 30 | ], |
---|
| 31 | ) |
---|
[0371870] | 32 | return logging.getLogger ('boottools') |
---|
[c5e9f68] | 33 | |
---|
[c79d715] | 34 | def _mount_rootfs (btrootfsimg, btrootfsmnt): |
---|
[95a24ac] | 35 | try: utils.mount (btrootfsimg, btrootfsmnt, opts=['-o', 'loop,offset=32256']) |
---|
[bd56977] | 36 | except: |
---|
| 37 | logger.error ('mount failed') |
---|
| 38 | sys.exit (1) |
---|
[45f533a] | 39 | |
---|
[c5e9f68] | 40 | def _get_pxepkg(): |
---|
[9a6fecf] | 41 | pxepkg = None |
---|
| 42 | cache = apt.cache_search (['gpxe', 'ipxe']) |
---|
| 43 | if cache['gpxe']: pxepkg = 'gpxe' |
---|
| 44 | if cache['ipxe']: pxepkg = 'ipxe' |
---|
| 45 | if pxepkg is None: |
---|
| 46 | logger.error ('neither gpxe nor ipxe found in apt cache') |
---|
[c5e9f68] | 47 | sys.exit (1) |
---|
| 48 | logger.info (f'PXE package is "{pxepkg}"') |
---|
| 49 | return pxepkg |
---|
| 50 | |
---|
[5ecf9f3] | 51 | def _mkrootfs (btrootfsimg, btrootfsmnt, btrootfsimglabel, btvirtualdisksize, bttargetdir, osarch): |
---|
| 52 | logger.info ('Stage 1.1 - create, partition and format the rootfs') |
---|
[c5e9f68] | 53 | rc = subprocess.run (f'file "{btrootfsimg}" |grep -q "partition 1 *: ID=0x83"', shell=True).returncode |
---|
| 54 | if (rc): ## 'file|grep' failed |
---|
[d1822ec] | 55 | try: btog.mkrootfs (btrootfsimg, btrootfsimglabel, btrootfsmnt, btvirtualdisksize, bttargetdir, osarch) |
---|
[c5e9f68] | 56 | except Exception as e: |
---|
| 57 | logger.error (str (e)) |
---|
[e6aa11e] | 58 | sys.exit (1) |
---|
| 59 | |
---|
[5ecf9f3] | 60 | def _debootstrap (btrootfsimg, btrootfsmnt, osarch, oscodename, oshttp): |
---|
| 61 | logger.info ('Stage 1.2 - debootstrap system') |
---|
[770a106] | 62 | _mount_rootfs (btrootfsimg, btrootfsmnt) |
---|
| 63 | try: os.stat (os.path.join (btrootfsmnt, 'etc')) |
---|
| 64 | except: |
---|
| 65 | logger.debug (f'stat failed, calling btog.debootstrap()') |
---|
[d1822ec] | 66 | try: btog.debootstrap (btrootfsimg, btrootfsmnt, osarch, oscodename, oshttp) |
---|
[c5e9f68] | 67 | except Exception as e: |
---|
[770a106] | 68 | utils.umount (btrootfsmnt) |
---|
[c5e9f68] | 69 | logger.error (str (e)) |
---|
[e6aa11e] | 70 | sys.exit (1) |
---|
[770a106] | 71 | utils.umount (btrootfsmnt) |
---|
[e6aa11e] | 72 | |
---|
[5ecf9f3] | 73 | def _initramfs_version (gitrelease, osrelease, curdir): |
---|
| 74 | utils.run (['sed', '-i', f'1 s/$/ {gitrelease} ({osrelease})/', f'{curdir}/includes/etc/initramfs-tools/scripts/VERSION.txt']) |
---|
[c5e9f68] | 75 | |
---|
[c79d715] | 76 | def _copy_og_files (btrootfsimg, btrootfsmnt, osdistrib, oscodename): |
---|
| 77 | _mount_rootfs (btrootfsimg, btrootfsmnt) |
---|
[bd56977] | 78 | builder = '/tmp/opengnsys/oglive_builder' |
---|
| 79 | og_shared = '/tmp/opengnsys/shared' |
---|
[5dfeffb] | 80 | btog.copy_og_files (builder, og_shared, btrootfsmnt, osdistrib, oscodename) |
---|
[95a24ac] | 81 | utils.umount (btrootfsmnt) |
---|
[c5e9f68] | 82 | |
---|
[c79d715] | 83 | def _chroot_tasks (cfgfile, curdir, osrelease, osarch): |
---|
[d1822ec] | 84 | logger.debug (f'running \'schroot --chroot IMGogclient -- {curdir}/chroot-tasks.py --osrelease "{osrelease}" --osarch "{osarch}"\'') |
---|
[c79d715] | 85 | stdout, _ = utils.run (['schroot', '--chroot', 'IMGogclient', '--', f'{curdir}/chroot-tasks.py', '--osrelease', osrelease, '--osarch', osarch, '--config', cfgfile]) |
---|
[0ab0e61] | 86 | ## this leaves initrd.img-6.8.0-31-generic and vmlinuz-6.8.0-31-generic in /tmp |
---|
[c5e9f68] | 87 | |
---|
[c79d715] | 88 | def _ssh_stuff (btrootfsimg, btrootfsmnt): |
---|
| 89 | _mount_rootfs (btrootfsimg, btrootfsmnt) |
---|
[e60695f] | 90 | btog.sysctl (btrootfsmnt) |
---|
[d1822ec] | 91 | btog.ssh_server (btrootfsmnt) |
---|
| 92 | btog.ssh_client (btrootfsmnt) |
---|
[95a24ac] | 93 | utils.umount (btrootfsmnt) |
---|
[0ab0e61] | 94 | ## the end result is: |
---|
| 95 | ## - there's a new key pair in the VM (or docker container), in /root/.ssh |
---|
| 96 | ## - there's another new key pair in the rootfs, in /var/lib/tftpboot/ogclient/ogclientmount/root/.ssh |
---|
| 97 | ## - the two pubkeys (one of each pair) end up being authorised in the rootfs, in /var/lib/tftpboot/ogclient/ogclientmount/root/.ssh/authorized_keys |
---|
[bd56977] | 98 | |
---|
[c79d715] | 99 | def _mkinitrd_squashfs_isofs (bttargetdir, osrelease, btrootfsimg, btrootfsmnt, pxepkg, isolinux_tpl, nameisoclient): |
---|
[5ecf9f3] | 100 | logger.info ('Stage 4.1 - Put initrd in place') |
---|
[c79d715] | 101 | _mount_rootfs (btrootfsimg, btrootfsmnt) |
---|
[d1822ec] | 102 | btog.move_initrd (bttargetdir, osrelease) |
---|
[e6aa11e] | 103 | |
---|
[5ecf9f3] | 104 | logger.info ('Stage 4.2 - make squash filesystem') |
---|
[d1822ec] | 105 | btog.mksquashfs (bttargetdir, btrootfsmnt) |
---|
[95a24ac] | 106 | utils.umount (btrootfsmnt) |
---|
[e6aa11e] | 107 | |
---|
[5ecf9f3] | 108 | logger.info ('Stage 4.3 - make iso filesystem') |
---|
[d1822ec] | 109 | btog.mkisofs (pxepkg, isolinux_tpl, bttargetdir, nameisoclient) |
---|
[e6aa11e] | 110 | |
---|
[c79d715] | 111 | def _main (cfgfile, config, type_client): |
---|
| 112 | isolinux_tpl = config['General'].get ('isolinux_template') |
---|
| 113 | btrootfsimglabel = config['General'].get ('rootfs_image_label') |
---|
[e6aa11e] | 114 | |
---|
[c79d715] | 115 | logger.info ('OpenGnsys CLIENT installation begins') |
---|
[e6aa11e] | 116 | |
---|
[c79d715] | 117 | fd = open (f'{curdir}/gitrelease', 'r') ## per the Dockerfile |
---|
| 118 | gitrelease = fd.readline().strip() |
---|
| 119 | fd.close() |
---|
[c5e9f68] | 120 | |
---|
[c79d715] | 121 | osdistrib, oscodename, osrelease, osarch, oshttp = btog.GetOsInfo (type_client) |
---|
| 122 | if osdistrib is None: |
---|
| 123 | logger.error ('GetOsInfo() failed') |
---|
| 124 | sys.exit (1) |
---|
| 125 | bttargetdir, btrootfsimg, btrootfsmnt, btvirtualdisksize = btog.GetVar (osarch) |
---|
| 126 | logger.info (':'.join ([osdistrib, oscodename, osrelease, osarch, oshttp])) |
---|
| 127 | |
---|
| 128 | ## this is convenient in case the previous run failed and we want to run this program again |
---|
| 129 | try: utils.umount (btrootfsmnt) |
---|
| 130 | except: pass |
---|
[f04ace2] | 131 | |
---|
[c79d715] | 132 | logger.info ('STAGE 1 - create and bootstrap rootfs') |
---|
| 133 | _mkrootfs (btrootfsimg, btrootfsmnt, btrootfsimglabel, btvirtualdisksize, bttargetdir, osarch) |
---|
| 134 | _debootstrap (btrootfsimg, btrootfsmnt, osarch, oscodename, oshttp) |
---|
[f04ace2] | 135 | |
---|
[c79d715] | 136 | logger.info ('STAGE 2 - copy files to the rootfs') |
---|
[1ee49df] | 137 | today = datetime.datetime.now(datetime.timezone.utc).strftime ('%Y%m%d') |
---|
| 138 | _initramfs_version (gitrelease+'_'+today, osrelease, curdir) |
---|
[c79d715] | 139 | _copy_og_files (btrootfsimg, btrootfsmnt, osdistrib, oscodename) |
---|
[feae768] | 140 | |
---|
[c79d715] | 141 | logger.info ('STAGE 3 - perform tasks within the chroot') |
---|
| 142 | _chroot_tasks (cfgfile, curdir, osrelease, osarch) |
---|
[feae768] | 143 | |
---|
[c79d715] | 144 | _ssh_stuff (btrootfsimg, btrootfsmnt) |
---|
[c5e9f68] | 145 | |
---|
[c79d715] | 146 | logger.info ('STAGE 4 - generate distribution files') |
---|
| 147 | pxepkg = _get_pxepkg() |
---|
[fe39c2d] | 148 | nameisoclient = '-'.join (['ogLive', oscodename, osrelease, osarch, gitrelease+'_'+today]) |
---|
[c79d715] | 149 | _mkinitrd_squashfs_isofs (bttargetdir, osrelease, btrootfsimg, btrootfsmnt, pxepkg, isolinux_tpl, nameisoclient) |
---|
[bd56977] | 150 | |
---|
[c79d715] | 151 | logger.info ('OpenGnsys installation finished') |
---|
[f04ace2] | 152 | |
---|
| 153 | |
---|
[c79d715] | 154 | if __name__ == '__main__': |
---|
| 155 | if os.getuid(): |
---|
| 156 | print ('ERROR: this program must run under root privileges!!', file=sys.stderr) |
---|
| 157 | sys.exit (1) |
---|
[f04ace2] | 158 | |
---|
[c79d715] | 159 | parser = argparse.ArgumentParser() |
---|
| 160 | parser.add_argument ('--loglevel', help='Log level', action='store') |
---|
| 161 | parser.add_argument ('--codename', help='OS codename', action='store') |
---|
| 162 | parser.add_argument ('--config', help='Path to configuration file', action='store') |
---|
| 163 | args = parser.parse_args() |
---|
| 164 | |
---|
| 165 | cfgfile = args.config or 'mkoglive.cfg' |
---|
| 166 | config = utils.read_config (cfgfile) |
---|
| 167 | if config is None: |
---|
| 168 | print ('ERROR: no configuration found', file=sys.stderr) |
---|
| 169 | sys.exit (1) |
---|
| 170 | cfg_loglevel = config['General'].get ('logging_level') |
---|
[c6cf378] | 171 | |
---|
[c79d715] | 172 | logger = _logging (args.loglevel or cfg_loglevel) |
---|
[960b66e] | 173 | |
---|
[c79d715] | 174 | _main (cfgfile, config, args.codename) |
---|