1 | #!/usr/bin/python3 |
---|
2 | |
---|
3 | import sys |
---|
4 | import os |
---|
5 | import logging |
---|
6 | import subprocess |
---|
7 | import glob |
---|
8 | import stat |
---|
9 | import shutil |
---|
10 | import argparse |
---|
11 | import datetime |
---|
12 | |
---|
13 | curdir = os.path.dirname (__file__) |
---|
14 | sys.path.insert (0, curdir) |
---|
15 | from boottools import utils, apt, btog |
---|
16 | |
---|
17 | os.chdir (curdir) |
---|
18 | |
---|
19 | def _logging (lvl='INFO', logfile='/tmp/boot-tools_installation.log'): |
---|
20 | numeric_level = getattr (logging, lvl.upper(), None) |
---|
21 | if numeric_level is None: |
---|
22 | numeric_level = getattr (logging, 'INFO') |
---|
23 | |
---|
24 | logging.basicConfig ( |
---|
25 | format='%(levelname)s %(asctime)s (%(funcName)s) %(message)s', |
---|
26 | level=numeric_level, |
---|
27 | handlers = [ |
---|
28 | logging.FileHandler (logfile), |
---|
29 | logging.StreamHandler (sys.stdout), |
---|
30 | ], |
---|
31 | ) |
---|
32 | return logging.getLogger ('boottools') |
---|
33 | |
---|
34 | def _mount_rootfs (btrootfsimg, btrootfsmnt): |
---|
35 | try: utils.mount (btrootfsimg, btrootfsmnt, opts=['-o', 'loop,offset=32256']) |
---|
36 | except: |
---|
37 | logger.error ('mount failed') |
---|
38 | sys.exit (1) |
---|
39 | |
---|
40 | def _get_pxepkg(): |
---|
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') |
---|
47 | sys.exit (1) |
---|
48 | logger.info (f'PXE package is "{pxepkg}"') |
---|
49 | return pxepkg |
---|
50 | |
---|
51 | def _mkrootfs (btrootfsimg, btrootfsmnt, btrootfsimglabel, btvirtualdisksize, bttargetdir, osarch): |
---|
52 | logger.info ('Stage 1.1 - create, partition and format the rootfs') |
---|
53 | rc = subprocess.run (f'file "{btrootfsimg}" |grep -q "partition 1 *: ID=0x83"', shell=True).returncode |
---|
54 | if (rc): ## 'file|grep' failed |
---|
55 | try: btog.mkrootfs (btrootfsimg, btrootfsimglabel, btrootfsmnt, btvirtualdisksize, bttargetdir, osarch) |
---|
56 | except Exception as e: |
---|
57 | logger.error (str (e)) |
---|
58 | sys.exit (1) |
---|
59 | |
---|
60 | def _debootstrap (btrootfsimg, btrootfsmnt, osarch, oscodename, oshttp): |
---|
61 | logger.info ('Stage 1.2 - debootstrap system') |
---|
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()') |
---|
66 | try: btog.debootstrap (btrootfsimg, btrootfsmnt, osarch, oscodename, oshttp) |
---|
67 | except Exception as e: |
---|
68 | utils.umount (btrootfsmnt) |
---|
69 | logger.error (str (e)) |
---|
70 | sys.exit (1) |
---|
71 | utils.umount (btrootfsmnt) |
---|
72 | |
---|
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']) |
---|
75 | |
---|
76 | def _copy_og_files (btrootfsimg, btrootfsmnt, osdistrib, oscodename): |
---|
77 | _mount_rootfs (btrootfsimg, btrootfsmnt) |
---|
78 | builder = '/tmp/opengnsys/oglive_builder' |
---|
79 | og_shared = '/tmp/opengnsys/shared' |
---|
80 | btog.copy_og_files (builder, og_shared, btrootfsmnt, osdistrib, oscodename) |
---|
81 | utils.umount (btrootfsmnt) |
---|
82 | |
---|
83 | def _chroot_tasks (cfgfile, curdir, osrelease, osarch): |
---|
84 | logger.debug (f'running \'schroot --chroot IMGogclient -- {curdir}/chroot-tasks.py --osrelease "{osrelease}" --osarch "{osarch}"\'') |
---|
85 | stdout, _ = utils.run (['schroot', '--chroot', 'IMGogclient', '--', f'{curdir}/chroot-tasks.py', '--osrelease', osrelease, '--osarch', osarch, '--config', cfgfile]) |
---|
86 | ## this leaves initrd.img-6.8.0-31-generic and vmlinuz-6.8.0-31-generic in /tmp |
---|
87 | |
---|
88 | def _ssh_stuff (btrootfsimg, btrootfsmnt): |
---|
89 | _mount_rootfs (btrootfsimg, btrootfsmnt) |
---|
90 | btog.sysctl (btrootfsmnt) |
---|
91 | btog.ssh_server (btrootfsmnt) |
---|
92 | btog.ssh_client (btrootfsmnt) |
---|
93 | utils.umount (btrootfsmnt) |
---|
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 |
---|
98 | |
---|
99 | def _mkinitrd_squashfs_isofs (bttargetdir, osrelease, btrootfsimg, btrootfsmnt, pxepkg, isolinux_tpl, nameisoclient): |
---|
100 | logger.info ('Stage 4.1 - Put initrd in place') |
---|
101 | _mount_rootfs (btrootfsimg, btrootfsmnt) |
---|
102 | btog.move_initrd (bttargetdir, osrelease) |
---|
103 | |
---|
104 | logger.info ('Stage 4.2 - make squash filesystem') |
---|
105 | btog.mksquashfs (bttargetdir, btrootfsmnt) |
---|
106 | utils.umount (btrootfsmnt) |
---|
107 | |
---|
108 | logger.info ('Stage 4.3 - make iso filesystem') |
---|
109 | btog.mkisofs (pxepkg, isolinux_tpl, bttargetdir, nameisoclient) |
---|
110 | |
---|
111 | def _main (cfgfile, config, type_client): |
---|
112 | isolinux_tpl = config['General'].get ('isolinux_template') |
---|
113 | btrootfsimglabel = config['General'].get ('rootfs_image_label') |
---|
114 | |
---|
115 | logger.info ('OpenGnsys CLIENT installation begins') |
---|
116 | |
---|
117 | fd = open (f'{curdir}/gitrelease', 'r') ## per the Dockerfile |
---|
118 | gitrelease = fd.readline().strip() |
---|
119 | fd.close() |
---|
120 | |
---|
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 |
---|
131 | |
---|
132 | logger.info ('STAGE 1 - create and bootstrap rootfs') |
---|
133 | _mkrootfs (btrootfsimg, btrootfsmnt, btrootfsimglabel, btvirtualdisksize, bttargetdir, osarch) |
---|
134 | _debootstrap (btrootfsimg, btrootfsmnt, osarch, oscodename, oshttp) |
---|
135 | |
---|
136 | logger.info ('STAGE 2 - copy files to the rootfs') |
---|
137 | today = datetime.datetime.now(datetime.timezone.utc).strftime ('%Y%m%d') |
---|
138 | _initramfs_version (gitrelease+'_'+today, osrelease, curdir) |
---|
139 | _copy_og_files (btrootfsimg, btrootfsmnt, osdistrib, oscodename) |
---|
140 | |
---|
141 | logger.info ('STAGE 3 - perform tasks within the chroot') |
---|
142 | _chroot_tasks (cfgfile, curdir, osrelease, osarch) |
---|
143 | |
---|
144 | _ssh_stuff (btrootfsimg, btrootfsmnt) |
---|
145 | |
---|
146 | logger.info ('STAGE 4 - generate distribution files') |
---|
147 | pxepkg = _get_pxepkg() |
---|
148 | nameisoclient = '-'.join (['ogLive', oscodename, osrelease, osarch, gitrelease+'_'+today]) |
---|
149 | _mkinitrd_squashfs_isofs (bttargetdir, osrelease, btrootfsimg, btrootfsmnt, pxepkg, isolinux_tpl, nameisoclient) |
---|
150 | |
---|
151 | logger.info ('OpenGnsys installation finished') |
---|
152 | |
---|
153 | |
---|
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) |
---|
158 | |
---|
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') |
---|
171 | |
---|
172 | logger = _logging (args.loglevel or cfg_loglevel) |
---|
173 | |
---|
174 | _main (cfgfile, config, args.codename) |
---|