From c9d9a3cc54ebe3dec927eee3b89ccf7d53d73aa9 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 25 Jul 2023 17:09:37 -0400 Subject: [PATCH] Add EL9 cloning support Main difference from EL8 is different grub strategy. To cope, capture the uuid of new /boot and manipulate the stub grub.cfg with the right uuid. In EL8, the efi has the main grub, EL9 changes to have /boot host the 'real' grub, causing us to have to modify the stub grub. --- .../profiles/default/scripts/image2disk.py | 59 ++++++++++++++++--- imgutil/imgutil | 2 +- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/confluent_osdeploy/el9-diskless/profiles/default/scripts/image2disk.py b/confluent_osdeploy/el9-diskless/profiles/default/scripts/image2disk.py index c4602d4b..c7d2deec 100644 --- a/confluent_osdeploy/el9-diskless/profiles/default/scripts/image2disk.py +++ b/confluent_osdeploy/el9-diskless/profiles/default/scripts/image2disk.py @@ -11,6 +11,8 @@ import struct import sys import subprocess +bootuuid = None + def get_next_part_meta(img, imgsize): if img.tell() == imgsize: return None @@ -53,10 +55,13 @@ class PartedRunner(): def __init__(self, disk): self.disk = disk - def run(self, command): + def run(self, command, check=True): command = command.split() command = ['parted', '-a', 'optimal', '-s', self.disk] + command - return subprocess.check_output(command).decode('utf8') + if check: + return subprocess.check_output(command).decode('utf8') + else: + return subprocess.run(command, stdout=subprocess.PIPE).stdout.decode('utf8') def fixup(rootdir, vols): devbymount = {} @@ -125,9 +130,12 @@ def fixup(rootdir, vols): sys.stdout.flush() for metafs in ('proc', 'sys', 'dev'): subprocess.check_call(['mount', '-o', 'bind', '/{}'.format(metafs), os.path.join(rootdir, metafs)]) - with open(os.path.join(rootdir, 'etc/sysconfig/grub')) as defgrubin: + grubsyscfg = os.path.join(rootdir, 'etc/sysconfig/grub') + if not os.path.exists(grubsyscfg): + grubsyscfg = os.path.join(rootdir, 'etc/default/grub') + with open(grubsyscfg) as defgrubin: defgrub = defgrubin.read().split('\n') - with open(os.path.join(rootdir, 'etc/sysconfig/grub'), 'w') as defgrubout: + with open(grubsyscfg, 'w') as defgrubout: for gline in defgrub: gline = gline.split() newline = [] @@ -136,8 +144,34 @@ def fixup(rootdir, vols): continue newline.append(ent) defgrubout.write(' '.join(newline) + '\n') - grubcfg = subprocess.check_output(['find', os.path.join(rootdir, 'boot'), '-name', 'grub.cfg']).decode('utf8').strip().replace(rootdir, '/') - subprocess.check_call(['chroot', rootdir, 'grub2-mkconfig', '-o', grubcfg]) + grubcfg = subprocess.check_output(['find', os.path.join(rootdir, 'boot'), '-name', 'grub.cfg']).decode('utf8').strip().replace(rootdir, '/').replace('//', '/') + grubcfg = grubcfg.split('\n') + if not grubcfg[-1]: + grubcfg = grubcfg[:-1] + if len(grubcfg) == 1: + grubcfg = grubcfg[0] + else: + for gcfg in grubcfg: + rgcfg = os.path.join(rootdir, gcfg[1:]) # gcfg has a leading / to get rid of + if os.stat(rgcfg).st_size > 256: + grubcfg = gcfg + else: + with open(rgcfg, 'r') as gin: + tgrubcfg = gin.read() + tgrubcfg = tgrubcfg.split('\n') + if 'search --no-floppy --fs-uuid --set=dev' in tgrubcfg[0]: + tgrubcfg[0] = 'search --no-floppy --fs-uuid --set=dev ' + bootuuid + with open(rgcfg, 'w') as gout: + for gcline in tgrubcfg: + gout.write(gcline) + gout.write('\n') + try: + subprocess.check_call(['chroot', rootdir, 'grub2-mkconfig', '-o', grubcfg]) + except Exception as e: + print(repr(e)) + print(rootdir) + print(grubcfg) + time.sleep(86400) newroot = None with open('/etc/shadow') as shadowin: shents = shadowin.read().split('\n') @@ -184,6 +218,7 @@ def had_swap(): return False def install_to_disk(imgpath): + global bootuuid lvmvols = {} deftotsize = 0 mintotsize = 0 @@ -224,7 +259,7 @@ def install_to_disk(imgpath): instdisk = diskin.read() instdisk = '/dev/' + instdisk parted = PartedRunner(instdisk) - dinfo = parted.run('unit s print') + dinfo = parted.run('unit s print', check=False) dinfo = dinfo.split('\n') sectors = 0 sectorsize = 0 @@ -366,6 +401,14 @@ def install_to_disk(imgpath): sys.stdout.write('\x1b[1K\rDone writing {0}'.format(vol['mount'])) sys.stdout.write('\n') sys.stdout.flush() + if vol['mount'] == '/boot': + tbootuuid = subprocess.check_output(['blkid', vol['targetdisk']]) + if b'UUID="' in tbootuuid: + bootuuid = tbootuuid.split(b'UUID="', 1)[1].split(b'"')[0].decode('utf8') + + + + subprocess.check_call(['umount', '/run/imginst/targ']) for vol in allvols: subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ/' + vol['mount']]) @@ -373,4 +416,4 @@ def install_to_disk(imgpath): if __name__ == '__main__': - install_to_disk(os.environ['mountsrc']) \ No newline at end of file + install_to_disk(os.environ['mountsrc']) diff --git a/imgutil/imgutil b/imgutil/imgutil index e7e55942..23d840e2 100644 --- a/imgutil/imgutil +++ b/imgutil/imgutil @@ -174,7 +174,7 @@ def capture_remote(args): subprocess.check_call(['rsync', __file__, '{0}:/run/imgutil/capenv/'.format(targ)]) finfo = subprocess.check_output(['ssh', targ, 'python3', '/run/imgutil/capenv/imgutil', 'getfingerprint']).decode('utf8') finfo = json.loads(finfo) - if finfo['oscategory'] != 'el8': + if finfo['oscategory'] not in ('el8', 'el9'): raise Exception('Not yet supported for capture: ' + repr(finfo)) oscat = finfo['oscategory'] subprocess.check_call(['ssh', '-o', 'LogLevel=QUIET', '-t', targ, 'python3', '/run/imgutil/capenv/imgutil', 'capturelocal'])