From 5dfbeef79c926db07a946a44edfd35f76af341fb Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Wed, 21 Jul 2021 11:15:42 -0400 Subject: [PATCH] Advance state of cloning Have imgutil complete the capture process, splitting work between target and repository. Provide hook through kcmdline to induce installtodisk. Have installimage reboot system cleanly when done. Have new /etc/confluent in cloned system. Hook for post scripts to execute. --- .../profiles/default/scripts/image2disk.py | 2 + .../profiles/default/scripts/imageboot.sh | 5 ++ .../profiles/default/scripts/installimage | 2 + imgutil/imgutil | 67 +++++++++++++------ 4 files changed, 55 insertions(+), 21 deletions(-) diff --git a/confluent_osdeploy/el8-diskless/profiles/default/scripts/image2disk.py b/confluent_osdeploy/el8-diskless/profiles/default/scripts/image2disk.py index b94fd8ca..007d1f47 100644 --- a/confluent_osdeploy/el8-diskless/profiles/default/scripts/image2disk.py +++ b/confluent_osdeploy/el8-diskless/profiles/default/scripts/image2disk.py @@ -112,6 +112,8 @@ def fixup(rootdir, vols): newcfg = ifcfg.split('/')[-1] newcfg = os.path.join(rootdir, 'etc/NetworkManager/system-connections/{0}'.format(newcfg)) shutil.copy2(ifcfg, newcfg) + shutil.rmtree(os.path.join(rootdir, 'etc/confluent/')) + shutil.copytree('/etc/confluent', os.path.join(rootdir, 'etc/confluent')) if policy: sys.stdout.write('Applying SELinux labeling...') sys.stdout.flush() diff --git a/confluent_osdeploy/el8-diskless/profiles/default/scripts/imageboot.sh b/confluent_osdeploy/el8-diskless/profiles/default/scripts/imageboot.sh index f014d92b..a7fda16c 100644 --- a/confluent_osdeploy/el8-diskless/profiles/default/scripts/imageboot.sh +++ b/confluent_osdeploy/el8-diskless/profiles/default/scripts/imageboot.sh @@ -101,4 +101,9 @@ curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/o chmod +x /sysroot/opt/confluent/bin/onboot.sh ln -s /etc/systemd/system/onboot.service /sysroot/etc/systemd/system/multi-user.target.wants/onboot.service cp /etc/confluent/functions /sysroot/etc/confluent/functions +if grep installtodisk /proc/cmdline > /dev/null; then + . /etc/confluent/functions + run_remote installimage + exec reboot -f +fi exec /opt/confluent/bin/start_root diff --git a/confluent_osdeploy/el8-diskless/profiles/default/scripts/installimage b/confluent_osdeploy/el8-diskless/profiles/default/scripts/installimage index ce51d1e3..5edb8f15 100644 --- a/confluent_osdeploy/el8-diskless/profiles/default/scripts/installimage +++ b/confluent_osdeploy/el8-diskless/profiles/default/scripts/installimage @@ -34,4 +34,6 @@ lvm vgchange -a n udevadm control -e chroot /sysroot /usr/lib/systemd/systemd-udevd --daemon chroot /sysroot bash -c "source /etc/confluent/functions; run_remote_python image2disk.py" +chroot /sysroot/run/imginst/targ bash -c "source /etc/confluent/functions; run_remote post.sh" +chroot /sysroot bash -c "umount \$(tac /proc/mounts|awk '{print \$2}'|grep ^/run/imginst/targ)" diff --git a/imgutil/imgutil b/imgutil/imgutil index 15609a8b..a73207be 100644 --- a/imgutil/imgutil +++ b/imgutil/imgutil @@ -120,6 +120,20 @@ def capture_fs(args): masker.mask('/root/.ssh/id_*') subprocess.check_call(['mksquashfs', '/run/imgutil/capin', fname + '.sfs', '-comp', 'xz']) +def capture_local_cleanup(): + shutil.rmtree('/usr/lib/dracut/modules.d/97confluent') + subprocess.check_call(['umount', '/run/imgutil/capout']) + +def build_boot_tree(targpath): + for dscript in glob.glob('/usr/lib/dracut/modules.d/97confluent/install*'): + os.chmod(dscript, 0o755) + kver = os.uname().release + mkdirp(os.path.join(targpath, 'boot/initramfs/')) + subprocess.check_call(['dracut', '-N', '--xz', '-m', 'confluent base terminfo', os.path.join(targpath, 'boot/initramfs/distribution')]) + shutil.copy2('/boot/vmlinuz-{}'.format(kver), os.path.join(targpath, 'boot/kernel')) + gather_bootloader(targpath) + + def capture_remote(opts, args): targ = args[0] outdir = args[1] @@ -140,13 +154,12 @@ def capture_remote(opts, args): raise Exception('Not yet supported for capture: ' + repr(finfo)) oscat = finfo['oscategory'] subprocess.check_call(['ssh', '-t', targ, 'python3', '/run/imgutil/capenv/imgutil', 'capturelocal']) - subprocess.check_call(['rsync', '-a', utillib, '{0}:/usr/lib/dracut/modules.d/97imgutil'.format(targ)]) - subprocess.check_call(['ssh', targ, 'chmod', '755', '/usr/lib/dracut/modules.d/97imgutil/install*']) - kernfile = subprocess.check_output(['ssh', targ, 'ls', '/boot/vmlinuz-$(uname -r)']) - subprocess.check_call(['ssh', targ, 'dracut', '-N', '--xz', '-m', '"imgutil base terminfo"', '/run/imgutil/capout/initramfs']) - # prep to receive result of above - mkdirp(os.path.join(outdir, 'boot/efi/boot')) - mkdirp(os.path.join(outdir, 'boot/initramfs/')) + utillib = __file__.replace('bin/imgutil', 'lib/imgutil') + utillib = os.path.join(utillib, '{}/dracut/'.format(oscat)) + subprocess.check_call(['rsync', '-a', utillib, '{0}:/usr/lib/dracut/modules.d/97confluent'.format(targ)]) + subprocess.check_call(['ssh', '-t', targ, 'python3', '/run/imgutil/capenv/imgutil', 'capturelocalboot']) + subprocess.check_call(['rsync', '-a', '{0}:/run/imgutil/capout/'.format(targ), outdir]) + subprocess.check_call(['ssh', '-t', targ, 'python3', '/run/imgutil/capenv/imgutil', 'capturelocalcleanup']) profname = os.path.basename(outdir) os.symlink('/var/lib/confluent/public/site/initramfs.cpio', os.path.join(outdir, 'boot/initramfs/site.cpio')) @@ -155,7 +168,6 @@ def capture_remote(opts, args): os.path.join(outdir, 'boot/initramfs/addons.cpio')) if os.path.exists('{}/profiles/default'.format(confdir)): copy_tree('{}/profiles/default'.format(confdir), outdir) - # now we need kernel, initramfs, rootimg.sfs, grub, and shim... label = '{0} {1} ({2})'.format(finfo['name'], finfo['version'], profname) @@ -198,6 +210,7 @@ def capture_system_back(args): with open('/run/imgutil/captmp/empty', 'w') as shadowout: pass i = 0 + todelete = [] with open('/run/imgutil/capout/rootimg.sfs', 'wb') as outimg: # Signature outimg.write(b'\x63\x7b\x9d\x26\xb7\xfd\x48\x30\x89\xf9\x11\xcf\x18\xfd\xff\xa1CONFLUENT_IMAGE') @@ -210,11 +223,13 @@ def capture_system_back(args): fname = os.path.join('/run/imgutil/capout', fname) run_constrained(capture_fs, (fs, fname)) isize = os.stat(fname + '.sfs').st_size + todelete.append(fname + '.sfs') outimg.write(struct.pack('!H', len(fs['mount'].encode('utf8')))) outimg.write(fs['mount'].encode('utf8')) fs['compressed_size'] = isize with open(fname + '.json', 'w') as fsinfout: fsinfout.write(json.dumps(fs)) + todelete.append(fname + '.json') jsize = os.stat(fname + '.json').st_size outimg.write(struct.pack('!I', jsize)) with open(fname + '.json','rb') as fsinfoin: @@ -241,6 +256,8 @@ def capture_system_back(args): pad = 4096 - (outimg.tell() % 4096) if pad < 4096: outimg.write(b'\x00' * pad) + for fname in todelete: + os.remove(fname) def create_yumconf(sourcedir): @@ -464,6 +481,10 @@ def main(): print(fingerprint_host().get_json()) elif args[0] == 'capturelocal': capture_system() + elif args[0] == 'capturelocalboot': + build_boot_tree('/run/imgutil/capout') + elif args[0] == 'capturelocalcleanup': + capture_local_cleanup() elif args[0] == 'exec': exec_root(opts, args[1:]) elif args[0] == 'pack': @@ -702,19 +723,7 @@ def pack_image(opts, args): os.path.join(outdir, 'boot/initramfs/site.cpio')) shutil.copyfile(kvermap[mostrecent], os.path.join(outdir, 'boot/kernel')) shutil.copyfile(initrdname, os.path.join(outdir, 'boot/initramfs/distribution')) - shimlocation = os.path.join(args[0], 'boot/efi/EFI/BOOT/BOOTX64.EFI') - if not os.path.exists(shimlocation): - shimlocation = os.path.join(args[0], 'usr/lib64/efi/shim.efi') - shutil.copyfile(shimlocation, os.path.join(outdir, 'boot/efi/boot/BOOTX64.EFI')) - grubbin = None - for candidate in glob.glob(os.path.join(args[0], 'boot/efi/EFI/*')): - if 'BOOT' not in candidate: - grubbin = os.path.join(candidate, 'grubx64.efi') - break - if not grubbin: - grubbin = os.path.join(args[0], 'usr/lib64/efi/grub.efi') - shutil.copyfile(grubbin, os.path.join(outdir, 'boot/efi/boot/grubx64.efi')) - shutil.copyfile(grubbin, os.path.join(outdir, 'boot/efi/boot/grub.efi')) + gather_bootloader(outdir, args[0]) subprocess.check_call(['mksquashfs', args[0], os.path.join(outdir, 'rootimg.sfs'), '-comp', 'xz']) oshandler = fingerprint_host(args[0]) @@ -745,6 +754,22 @@ def pack_image(opts, args): except KeyError: pass +def gather_bootloader(outdir, rootpath='/'): + shimlocation = os.path.join(rootpath, 'boot/efi/EFI/BOOT/BOOTX64.EFI') + if not os.path.exists(shimlocation): + shimlocation = os.path.join(rootpath, 'usr/lib64/efi/shim.efi') + mkdirp(os.path.join(outdir, 'boot/efi/boot')) + shutil.copyfile(shimlocation, os.path.join(outdir, 'boot/efi/boot/BOOTX64.EFI')) + grubbin = None + for candidate in glob.glob(os.path.join(rootpath, 'boot/efi/EFI/*')): + if 'BOOT' not in candidate: + grubbin = os.path.join(candidate, 'grubx64.efi') + break + if not grubbin: + grubbin = os.path.join(rootpath, 'usr/lib64/efi/grub.efi') + shutil.copyfile(grubbin, os.path.join(outdir, 'boot/efi/boot/grubx64.efi')) + shutil.copyfile(grubbin, os.path.join(outdir, 'boot/efi/boot/grub.efi')) +