diff --git a/imgutil/confluent_imgutil.spec.tmpl b/imgutil/confluent_imgutil.spec.tmpl index 3d5f90ca..47ca7232 100644 --- a/imgutil/confluent_imgutil.spec.tmpl +++ b/imgutil/confluent_imgutil.spec.tmpl @@ -34,7 +34,7 @@ mkdir -p opt/confluent/lib/imgutil mkdir -p opt/confluent/bin mv imgutil opt/confluent/bin/ chmod a+x opt/confluent/bin/imgutil -mv ubuntu* suse15 el7 el9 el10 el8 opt/confluent/lib/imgutil/ +mv ubuntu* suse15 suse16 el7 el9 el10 el8 opt/confluent/lib/imgutil/ mkdir -p opt/confluent/share/licenses/confluent_imgutil cp LICENSE opt/confluent/share/licenses/confluent_imgutil diff --git a/imgutil/imgutil b/imgutil/imgutil index cfa5492d..c03992a7 100644 --- a/imgutil/imgutil +++ b/imgutil/imgutil @@ -588,9 +588,10 @@ class SuseHandler(OsHandler): def __init__(self, name, version, arch, args): if not isinstance(version, str): version = version.decode('utf8') - if not version.startswith('15.'): + major = version.split('.', 1)[0] + if major not in ('15', '16'): raise Exception('Unsupported Suse version {}'.format(version)) - self.oscategory = 'suse15' + self.oscategory = 'suse{0}'.format(major) super().__init__(name, version, arch, args) self.zyppargs = [] self.sources = [] @@ -610,6 +611,15 @@ class SuseHandler(OsHandler): if enterprise: self.sources.append('file://' + os.path.join(sourcepath, 'Product-HPC')) + def zypper_cmd(self): + # 16's stock repo urls are written in terms of ${releasever}, which + # zypper resolves from the target root's os-release. That file does not + # exist yet when the first packages are installed, so say it outright. + cmd = ['zypper', '-R', self.targpath] + if self.version: + cmd.extend(['--releasever', self.version]) + return cmd + def prep_root(self, args): gpgkeys = [] mkdirp(self.targpath) @@ -617,8 +627,19 @@ class SuseHandler(OsHandler): gpgkeys = glob.glob('/usr/lib/rpm/gnupg/keys/*.asc') targzypp = os.path.join(self.targpath, 'etc/zypp') mkdirp(targzypp) - shutil.copytree( - '/etc/zypp/repos.d/', os.path.join(targzypp, 'repos.d')) + # 16 declares its repositories through a zypper service backed by + # a package-provided directory the target root does not have. Drop + # the service line so each definition stands on its own, otherwise + # zypper discards every one of them as an orphan. + targrepos = os.path.join(targzypp, 'repos.d') + mkdirp(targrepos) + for repofile in glob.glob('/etc/zypp/repos.d/*'): + with open(repofile) as repoin: + cfg = [line for line in repoin.read().splitlines() + if not line.startswith('service=')] + with open(os.path.join( + targrepos, os.path.basename(repofile)), 'w') as repoout: + repoout.write('\n'.join(cfg) + '\n') idx = 1 for source in self.sources: if not source: @@ -626,7 +647,7 @@ class SuseHandler(OsHandler): if source.startswith('file://'): gpgpath = source.replace('file://', '') gpgkeys.extend(glob.glob(os.path.join(gpgpath, '*/gpg-pubkey*.asc'))) - subprocess.check_call(['zypper', '-R', self.targpath, 'ar', source, 'source-{}'.format(idx)]) + subprocess.check_call(self.zypper_cmd() + ['ar', source, 'source-{}'.format(idx)]) idx += 1 if gpgkeys: addkeycmd = ['rpm', '--root', self.targpath, '--import'] + gpgkeys @@ -638,7 +659,7 @@ class SuseHandler(OsHandler): if not source.startswith('/') and os.path.exists(os.path.abspath(source)): source = os.path.abspath(source) source = 'file://' + source - subprocess.check_call(['zypper', '-R', self.targpath, 'ar', source, 'source-{}'.format(idx)]) + subprocess.check_call(self.zypper_cmd() + ['ar', source, 'source-{}'.format(idx)]) idx += 1 mydir = get_mydir(self.oscategory) mkdirp(os.path.join(self.targpath, 'usr/lib/dracut/modules.d')) @@ -652,10 +673,15 @@ class SuseHandler(OsHandler): cmd.extend(glob.glob(os.path.join(targdir, '*'))) subprocess.check_call(cmd) if self._interactive: - subprocess.check_call(['zypper', '-R', self.targpath, 'install'] + self.zyppargs) + subprocess.check_call(self.zypper_cmd() + ['install'] + self.zyppargs) else: - subprocess.check_call(['zypper', '-n', '-R', self.targpath, 'install'] + self.zyppargs) - os.symlink('/usr/lib/systemd/system/sshd.service', os.path.join(self.targpath, 'etc/systemd/system/multi-user.target.wants/sshd.service')) + subprocess.check_call(self.zypper_cmd() + ['-n', 'install'] + self.zyppargs) + # 16's presets enable sshd already, so the link is often there + sshdwant = os.path.join( + self.targpath, 'etc/systemd/system/multi-user.target.wants/sshd.service') + mkdirp(os.path.dirname(sshdwant)) + if not os.path.lexists(sshdwant): + os.symlink('/usr/lib/systemd/system/sshd.service', sshdwant) with open(os.path.join(self.targpath, 'etc/permissions.local'), 'a') as permout: permout.write( '/usr/lib/ssh/ssh-keysign root:ssh_keys 2711\n' @@ -675,7 +701,12 @@ class SuseHandler(OsHandler): if os.path.exists(os.path.join(self.targpath, 'sbin/mkinitrd')): args.cmd = ['mkinitrd'] else: - args.cmd = ['dracut', '-f'] + # dracut alone would build for the running kernel, which is the + # build host's, not the one just installed into the image. 16 has + # no mkinitrd to work that out, so name the kernel and the output. + kver = sorted(os.listdir(os.path.join(self.targpath, 'lib/modules')), + key=versionize_string)[-1] + args.cmd = ['dracut', '-f', '/boot/initrd-{0}'.format(kver), kver] run_constrainedx(fancy_chroot, (args, self.targpath)) @@ -1297,7 +1328,7 @@ def fingerprint_source_suse(files, sourcepath, args): if ': ' not in line: continue key, val = line.split(': ') - if key == 'category' and val == 'suse15': + if key == 'category' and val in ('suse15', 'suse16'): issuse = True if key == 'name': osname, ver, arch = val.split('-') @@ -1739,6 +1770,15 @@ def gather_bootloader(outdir, rootpath='/'): shimlocation = os.path.join(rootpath, 'usr/lib/shim/shimx64.efi.signed') if not os.path.exists(shimlocation): shimlocation = os.path.join(rootpath, 'usr/lib/shim/shimaa64.efi.signed.latest') + if not os.path.exists(shimlocation): + # SUSE 16 moved the efi payloads out of /usr/lib64/efi + shimlocation = os.path.join(rootpath, 'usr/share/efi/x86_64/shim.efi') + shimdestfilename = 'BOOTX64.EFI' + if not os.path.exists(shimlocation): + aa64shim = os.path.join(rootpath, 'usr/share/efi/aarch64/shim.efi') + if os.path.exists(aa64shim): + shimlocation = aa64shim + shimdestfilename = 'BOOTAA64.EFI' mkdirp(os.path.join(outdir, 'boot/efi/boot')) shutil.copyfile(shimlocation, os.path.join(outdir, 'boot/efi/boot/{0}'.format(shimdestfilename))) for maybemokmanager in glob.glob(os.path.join(rootpath, 'boot/efi/EFI/*/mmx64.efi')): @@ -1747,6 +1787,8 @@ def gather_bootloader(outdir, rootpath='/'): else: if os.path.exists(os.path.join(rootpath, 'usr/lib/shim/mmx64.efi')): shutil.copyfile(os.path.join(rootpath, 'usr/lib/shim/mmx64.efi'), os.path.join(outdir, 'boot/efi/boot/mmx64.efi')) + elif os.path.exists(os.path.join(rootpath, 'usr/share/efi/x86_64/MokManager.efi')): + shutil.copyfile(os.path.join(rootpath, 'usr/share/efi/x86_64/MokManager.efi'), os.path.join(outdir, 'boot/efi/boot/mmx64.efi')) for maybemokmanager in glob.glob(os.path.join(rootpath, 'boot/efi/EFI/*/mmaa64.efi')): shutil.copyfile(maybemokmanager, os.path.join(outdir, 'boot/efi/boot/mmaa64.efi')) break @@ -1769,6 +1811,8 @@ def gather_bootloader(outdir, rootpath='/'): grubbin = os.path.join(rootpath, 'usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed') if not os.path.exists(grubbin): grubbin = os.path.join(rootpath, 'usr/lib/grub/arm64-efi/monolithic/grubaa64.efi') + if not os.path.exists(grubbin): + grubbin = os.path.join(rootpath, 'usr/share/efi/x86_64/grub.efi') if not os.path.exists(grubbin): grubs = os.path.join(rootpath, 'boot/efi/EFI/*/grubx64.efi') grubs = glob.glob(grubs) diff --git a/imgutil/suse16/dracut/install b/imgutil/suse16/dracut/install new file mode 100644 index 00000000..67819080 --- /dev/null +++ b/imgutil/suse16/dracut/install @@ -0,0 +1,39 @@ +dracut_install mktemp +dracut_install /usr/lib64/libtss2-tcti-device.so.* +dracut_install tpm2_create tpm2_pcrread tpm2_createpolicy tpm2_createprimary +dracut_install tpm2_load tpm2_unseal tpm2_getcap tpm2_evictcontrol +dracut_install tpm2_pcrextend tpm2_policypcr tpm2_flushcontext tpm2_startauthsession +dracut_install curl openssl tar cpio gzip lsmod ethtool xz lsmod ethtool +dracut_install modprobe touch echo cut wc bash uniq grep ip hostname +dracut_install awk egrep dirname expr sort +dracut_install ssh sshd reboot parted mkfs mkfs.ext4 mkfs.xfs xfs_db mkswap +dracut_install efibootmgr uuidgen +dracut_install du df ssh-keygen scp clear dhclient +dracut_install /lib64/libnss_dns.so.2 # glibc dropped the versioned name +dracut_install /lib64/libnss_compat* +dracut_install /usr/lib64/libnl-3.so.200 +# 16 ships the stock config defaults under /usr/etc; only services stayed put +dracut_install /usr/etc/nsswitch.conf /etc/services /usr/etc/protocols +dracut_install chmod whoami head tail basename tr +dracut_install arping logger hostnamectl # 16 moved arping to /usr/bin +inst /bin/bash /bin/sh +dracut_install /lib64/libfuse.so.2 # symlink, dracut brings the target along +# urlmount still links libpthread, which since glibc 2.34 is an empty stub that +# nothing else in the initramfs pulls in +dracut_install /lib64/libpthread.so.0 +dracut_install chown chroot dd expr kill parted rsync sort blockdev findfs insmod lvm +dracut_install /usr/lib/udev/rules.d/10-dm.rules /usr/sbin/dmsetup /usr/lib/udev/rules.d/95-dm-notify.rules +dracut_install /usr/lib/systemd/network/99-default.link +dracut_install losetup # multipart support + +#this would be nfs with lock, but not needed, go nolock +#dracut_install mount.nfs rpcbind rpc.statd /etc/netconfig sm-notify +#dracut_install mount.nfs /etc/netconfig +inst /usr/lib/dracut/modules.d/40network/net-lib.sh /lib/net-lib.sh + + + +# network mount, and disk imaging helpers can come from a second stage +# this is narrowly focused on getting network up and fetching images +# and those images may opt to do something with cloning or whatever + diff --git a/imgutil/suse16/dracut/installkernel b/imgutil/suse16/dracut/installkernel new file mode 100644 index 00000000..46f2c335 --- /dev/null +++ b/imgutil/suse16/dracut/installkernel @@ -0,0 +1,18 @@ +#!/bin/bash +instmods nfsv3 nfs_acl nfsv4 dns_resolver lockd fscache sunrpc +instmods e1000 e1000e igb sfc mlx5_ib mlx5_core mlx4_en cxgb3 cxgb4 tg3 bnx2 bnx2x bna ixgb ixgbe qlge mptsas mpt2sas mpt3sas megaraid_sas ahci xhci-hcd sd_mod pmcraid be2net vfat ext3 ext4 usb_storage scsi_wait_scan ipmi_si ipmi_devintf qlcnic xfs +instmods nvme +instmods cdc_ether r8152 +instmods r8169 +instmods vmxnet3 virtio_net +instmods virtio_scsi vmw_pvscsi +instmods xen-netfront xen-blkfront xen-pcifront +instmods mptctl +instmods mlx4_ib mlx5_ub ib_umad ib_ipoib +instmods ice i40e hfi1 bnxt_en qed qede +instmods dm-mod dm-log raid0 raid1 raid10 raid456 dm-raid dm-thin-pool dm-crypt dm-snapshot linear dm-era +# imgutil packs encrypted by default and dm-crypt cannot allocate the transform +# without these; the stock dracut crypt module is not in the diskless image +instmods aes_generic aesni_intel xts crypto_simd +# nfs root and optionally gocryptfs +instmods fuse overlay squashfs loop zram diff --git a/imgutil/suse16/pkglist b/imgutil/suse16/pkglist new file mode 100644 index 00000000..7f7e582a --- /dev/null +++ b/imgutil/suse16/pkglist @@ -0,0 +1,22 @@ +hostname +curl +irqbalance +less +sudo +tuned +xfsprogs +e2fsprogs +chrony net-tools rsync tar util-linux python3 tar dracut ethtool parted openssl bash rpm iputils lvm2 efibootmgr attr +dhcp-client +zypper +grub2-x86_64-efi +libfuse2 +openssh +shim +kernel-default +tpm2.0-tools +# tpm2.0-tools only requires tctildr, and the diskless dracut module +# installs the device tcti explicitly +libtss2-tcti-device0 +NetworkManager # 16 dropped wicked; confignet writes nmconnections +vim