Kickstart with isolinux fails, same kickstart works with EFI

I have been stuck now for a while with a problem. I use kickstart files to set up custom configurations for our systems. I have about 50 systems that will be updated, so it needs to be efficient enough that a tech can oversee it. We have some systems that do not support EFI boot, and I have installed AlmaLinux 8.x many times with a custom edit of the isolinux.cfg file to add a kickstart submenu, along with the mechanisms to automatically select the disk and partitioning setup for a scheme that is proof to overflow of log files, as each area that can cause a problem is on its own partition. This mean lots of partitions (usually 12 or 13), but since it is automatic, it is not an issue, I also have a mapping of MAC address to IP4 static addressing and system name that is also handled by a section of the same script. Other than the rational, though it has little to do with the problem.
I have created and vetted the kickstart for AlmaLinux 9.4 using the same media with EFI boot and Grub2, with no problems. When I then test on the same hardware using the insecure boot option, and isolinux, I get the install to start, but it stops and from the dracut emergency shell I was able to check what the kernel through it got for a command line, which looks OK to me. The error from the journactl log indicates that it timed out on the reporting of devices, including root and the kickstart. I verified that is was not due to the changes I made to isolinux.cfg by building a new USB stick with the same image using rufus, with a persistent partition also on that device, which is where I put the kickstart files. I was able to use this image to build a system on my test hardware using the manual install procedure, and taking the resulting kickstart, and with a minor tweak required for the partitioning section of the Kickstart attempted to use it to build the system. First I verified that the Kickstart would work using EFI boot, which it did. I then started it again, opting for the bios boot, with the same result.
Has anyone else seen this problem with 9.4?

The change to the boot kernel line to do the quickstart in isolinux.cfg is:

label autonet
  menu label ^Install AlmaLinux 9.4 (autonetwork)
  kernel vmlinuz
  append initrd=initrd.img inst.repo=hd:LABEL=ALMALINUX-9 inst.ks=hd:persistence:ks.cfg

persistence is the label of the persistent partition on the install media.

The kernel command line from the journal:

BOOT_IMAGE=vmlinuz initrd=initrd.img inst.repo=hd:LABEL=ALMALINUX-9 inst.ks=hd:persistence:/ks.cfg

There is a warning that BOOT_IMAGE=vmlinuz was an unknown
kernel command, and it was being passed to user space.

I am going to check the journal for the command thine that occurs during an EFI secure boot.

Anyone have a suggestion?

Is this something you have written or found somewhere? Are you willing to share this part? I am interested because I think more people face the problem that they cannot use auto partition.

The only problem i got with bios boot is that after the install finished I had to physically remove the media. Although I used the option “reboot --eject”.

Unfortunately no solution.
But it might help others to find a solution of you share the log files generated by the installer.

I wrote both the Auto-partition and preset network code myself. I came across the list of partitions to isolate from each other to prevent runaway log files and other problems like that from crashing a system somewhere in my reading posts on various forums.

I was already used to using kickstart for provisioning systems, using network boot and a local mirror. If found two places that are populated with information very early by the kernel. First is /dev/disk/by-* series of links, that map the bios names to immutable indicators like disk label, UUID, hardware path to the device (pci:…:…), etc. I had been using the mount label= format for a while, and I also wanted a way to insure that the USB drive that I was using didn’t end up as part of the installed file systems. I also found that /sys/block has all of the devices, and has the information like drive size, etc.
With that in hand, I then proceeded to write my rules for the file system as a python script that is run as a %pre of a kickstart. I also wrote a python script to do the network identification, after raiding the arp caches on several machines to get the MAC to IP mapping. Once I have the IP, the hostname is trivial.

log fragments and details from the rdsosreport.txt (will expire on 9/18/2024)
first 190 lines of rd sos report
/dev/disk/* listing
first bit of the log fro sos report, showing command line
Second chunk of log, showign the beginning of the error message loop

Will continue in next post with sources for the autopartition code,

%pre script used for automatically determining partition sizes for kickstart
–Validated for AlmaLinux 9.4, should also work for Almalinux 8

%pre --interpreter=/usr/libexec/platform-python
#
#
from pathlib import Path
import parted as Part
import os.path as Ospath
DEBUG=False
DEBUG=True

def SizeGB(dev):
    s = dev.as_posix()
    with open(s+'/size') as fd:
        sz=int(fd.read())//2**21
    return(sz)
if DEBUG:
    print('\n\n*************************************************************\n')
paths =  Path('/sys/block').glob('*')
lines = list(paths)
lines.sort()  # this will prioritze NVME #sort in place
sizes = {}
for l in lines:
    n=l.name
    sizes[n] = SizeGB(l)
if DEBUG:
    print(sizes)
    print('\n\n*************************************************************\n')
devs = {}
#print(lines)
paths= Path('/dev/disk/by-label').glob('*')
lines = list(paths)
for l in lines:
    s=l.as_posix()
    if s.startswith('/dev/disk/by-label/ALMALINUX'):
        Source = l
        source = l.resolve().as_posix()  # partition of source
        d,s = Ospath.split(source)
        sourceDevice = source.rstrip('0123456789')  # will be something like sde
#for d,s in devs.items():
#    print(d,s)
if DEBUG:
    print(sourceDevice)
# only look at targets that are not usb
targets = {}
ignore = {}
ipaths = list(Path('/dev/disk/by-id').glob('*'))
#print(ipaths)
for i in range(len(ipaths)):
    p = ipaths[i]
    l=p.as_posix()
    rp = p.resolve()
    if not(l.endswith(('-part','-par'),-7,-2)):
        print(l,' >> ',str(rp))
        if 'usb-' in l:
            ignore[rp.name] = rp
            continue
        if sizes[rp.name] < 200:
            ignore[rp.name] = rp
            continue
        targets[rp.name] = rp
rem =[]
for name in targets.keys():
    if name in ignore.keys():
        print('eliminate %s from targets'%name)
        rem.append(name) # use list to keep track, will remove next block
for name in rem:
    n = targets.pop(name)


if DEBUG:
    print('\n-------------------------------------------------------------\n')
    print(targets)        
    print('\n###########################################################\n')
    print(ignore)

#
# now have a small bios level set of devices to install to.
# I have deliberatly removed usb devices, as they
# cannot be depended on to remain with the system.  
# They are at least 200GiB in size.
#
# choose the smallest. or the first in line if al equal, for root/boot
size = 1e12
mxsz =0
homedev=None
dest = None
count =0
for d in targets.keys():
    if sizes[d] < size:
        size = sizes[d]
        dest = targets[d]
        dindex = d
    if sizes[d] >= mxsz:
        mxsz = sizes[d]
        homedev = targets[d]
        count +=1
# determine swap size
with open('/proc/meminfo') as fd:
    l=fd.readline()
ll = l.split()
sz=int(ll[1])//1e6  # GB ram, 
# allocate min 4GB for swap, to a max of 24GB mem+swap
# with swap no larger than 2x memory
# this limits swap to 16Gb at 8Gb ram
# minimal swap in low disk size limit
# if disk space is not an issue, limit at mem/2
swap = min(max(4,(24-sz)),2*sz)   # in Gb
maxopt=''
if(homedev == dest):
    maxopt = '--maxsize=131072 '
if (mxsz >400) or (homedev != dest):
    swap = max(swap,sz//2)
  
if DEBUG:
    print('destination disk',dest,' size:',sizes[dindex],'GiB')
    print('home: ',homedev,' swap: ',swap)
diskmb = sizes[dindex]*2**10 # disk size in MiB
# sizes in MB
'''
biosboot=2
#swap calc'd above, max 32GiB
boot=2 000  # 2GB
bootefi=400
root=48 000   153 600
usr=48 000  200 000
var=32 000  150 000
log=48 000
vartmp=20 000
audit=20 000
tmp=20 000
opt=32 000 150 000 
home=10 000 remander
'''
#
ign = list(ignore.keys())
sd =[]
for n in ign:
    if n[1] == 'd':
        sd.append(n)
sd_str= ','.join(sd)
##
fd = sys.stdout
print('ignoredisk --drives=%s'%(sd_str),file=fd)
print('zerombr',file=fd)
print('bootloader --location=mbr   --drive=%s'%(dest),file=fd)
if homedev != dest:
    print('clearpart --all --initlabel --disklabel=gpt --drives=%s,%s'%(dest,homedev),file=fd)
else:    
    print('clearpart --all --initlabel --disklabel=gpt --drives=%s'%(dest),file=fd)
# Disk partitioning information
print('part biosboot --fstype="biosboot" --size=2 --ondisk=%s'%(dest),file=fd)
print('part /boot/efi --fstype="efi" --size=512 --ondisk=%s'%(dest),file=fd)
print('part swap  --fstype="swap" --hibernation --maxsize=%d  --ondisk=%s'%(swap,dest),file=fd)
print('part /boot --fstype="xfs" --label=BOOT --size=2048   --ondisk=%s'%(dest),file=fd)
print('part /     --fstype="xfs" --label=ROOT --size=24568 --maxsize=131072 --grow --ondisk=%s'%(dest),file=fd)
print('part /usr  --fstype="xfs" --label=USR  --size=49152 --maxsize=226815 --grow --ondisk=%s'%(dest),file=fd)
print('part /var  --fstype="xfs" --label=VAR  --size=20480 --maxsize=163840 --grow --ondisk=%s'%(dest),file=fd)
print('part /var/log  --fstype="xfs" --label=VAR  --size=20480 --maxsize=40960 --grow --ondisk=%s'%(dest),file=fd)
print('part /tmp  --fstype="xfs" --label=TMP  --size=10240 --maxsize=32768 --grow --ondisk=%s'%(dest),file=fd)
print('part /var/tmp  --fstype="xfs" --label=VARTMP  --size=10240 --maxsize=32768 --grow --ondisk=%s'%(dest),file=fd)
print('part /var/log/audit  --fstype="xfs" --label=AUDIT  --size=10240 --maxsize=20480 --grow --ondisk=%s'%(dest),file=fd)
print('part /opt  --fstype="xfs" --label=OPT  --size=10240 %s--grow --ondisk=%s'%(maxopt,dest),file=fd) # dump rest of disk in /opt if home on separte device
print('part /home --fstype="xfs" --label=HOME --size=10240 --grow --ondisk=%s'%(homedev),file=fd)
#
ign = list(ignore.keys())
sd =[]
for n in ign:
    if n[1] == 'd':
        sd.append(n)
sd_str= ','.join(sd)
##
fd = open('/tmp/parts.cfg',mode='w')
print('ignoredisk --drives=%s'%(sd_str),file=fd)
print('zerombr',file=fd)
print('bootloader --location=mbr   --drive=%s'%(dest),file=fd)
if homedev != dest:
    print('clearpart --all --initlabel --disklabel=gpt --drives=%s,%s'%(dest,homedev),file=fd)
else:    
    print('clearpart --all --initlabel --disklabel=gpt --drives=%s'%(dest),file=fd)
# Disk partitioning information
print('part biosboot --fstype="biosboot" --size=2 --ondisk=%s'%(dest),file=fd)
print('part /boot/efi --fstype="efi" --size=512 --ondisk=%s'%(dest),file=fd)
print('part swap  --fstype="swap" --hibernation --maxsize=%d  --ondisk=%s'%(swap,dest),file=fd)
print('part /boot --fstype="xfs" --label=BOOT --size=2048   --ondisk=%s'%(dest),file=fd)
print('part /     --fstype="xfs" --label=ROOT --size=24568 --maxsize=131072 --grow --ondisk=%s'%(dest),file=fd)
print('part /usr  --fstype="xfs" --label=USR  --size=49152 --maxsize=226815 --grow --ondisk=%s'%(dest),file=fd)
print('part /var  --fstype="xfs" --label=VAR  --size=20480 --maxsize=163840 --grow --ondisk=%s'%(dest),file=fd)
print('part /var/log  --fstype="xfs" --label=VAR  --size=20480 --maxsize=40960 --grow --ondisk=%s'%(dest),file=fd)
print('part /tmp  --fstype="xfs" --label=TMP  --size=10240 --maxsize=32768 --grow --ondisk=%s'%(dest),file=fd)
print('part /var/tmp  --fstype="xfs" --label=VARTMP  --size=10240 --maxsize=32768 --grow --ondisk=%s'%(dest),file=fd)
print('part /var/log/audit  --fstype="xfs" --label=AUDIT  --size=10240 --maxsize=20480 --grow --ondisk=%s'%(dest),file=fd)
print('part /opt  --fstype="xfs" --label=OPT  --size=10240 %s--grow --ondisk=%s'%(maxopt,dest),file=fd) # dump rest of disk in /opt if home on separte device
print('part /home --fstype="xfs" --label=HOME --size=10240 --grow --ondisk=%s'%(homedev),file=fd)
fd.close()
%end

%pre script for configuring network given MAC address
The IP addresses and host names shown here are for example only

%pre --interpreter=/usr/libexec/platform-python
#
import subprocess 
cache='''00:21:9b:73:f9:a3 10.0.0.155
7c:5c:f8:f0:3f:a0 10.0.0.166
00:1b:21:bb:71:a4 10.0.0.100
'''
lines = cache.splitlines()
mac_ip = {}
for l in lines:
    ll = l.split()
    mac_ip[ll[0]] = ll[1]

names='''00:21:9b:73:f9:a3 test1.ipa.localdomain
7c:5c:f8:f0:3f:a0 test2.ipa.localdomain
00:1b:21:bb:71:a4 test3.ipa.localdomain
'''
lines = names.splitlines()
mac_name = {}
for l in lines:
    ll = l.split()
    mac_name[ll[0]] = ll[1]
#
#
# get mac address using ip

args=['ip','-br','link','show']
#addresses = Sub.CompletedProcess()
addresses = subprocess.run(args, stdout=subprocess.PIPE)
lines=str(addresses.stdout,encoding='utf-8').splitlines()
interfaces =dict()  # dictionary of interfaces indexed by MAC
for l in lines:
    ll = l.split()
    if ll[0] not in ['lo','virbr0']:  # only interested in hardware
        interfaces[ll[2]] = ll[0]
systems = {}
ip = None
sysname = None
for (k,v) in interfaces.items():
    if k in mac_ip:
        mac=k
        ip = mac_ip.get(k)
        sysname = mac_name.get(k)
        break
dev=interfaces.pop(k)
import socket
# for gateway and nameservers, get ip masked for gateway, and
# key off of 1st quad for nameserver string
nip = socket.inet_aton(ip)
adr = [c for c in nip]
nip = socket.inet_aton('255.255.255.0')
mask= [c for c in nip]
gate = []
for i in range(4):
    gate.append(adr[i] & mask[i])
gate[3] += 1
gateway = "%d.%d.%d.%d"%tuple(gate)
if gate[0] < 11:
    nservers = '75.75.75.75,75.75.76.76'
else:
    nservers = '164.54.208.9'
import sys
fd = sys.stdout
# print to log
if ip:
    print('network --bootproto=static --ip=%(ip)s --netmask=255.255.255.0 --hostname=%(sysname)s --device=%(mac)s --bindto=mac --activate --gateway=%(gate)s --nameserver=%(dns)s --onboot=True'
       %{'ip':ip,'sysname':sysname,'mac':mac,'gate':gateway,'dns':nservers},file=fd)
for (k,v) in interfaces.items():
    print('network --bootproto=dhcp --device=%s --bindto=mac --onboot=False --nodefroute --notksdevice'%(k,),file=fd)
with open('/tmp/net.cfg','w') as fd:
    if ip:
        print('network --bootproto=static --ip=%(ip)s --netmask=255.255.255.0 --hostname=%(sysname)s --device=%(mac)s --bindto=mac --activate --gateway=%(gate)s --nameserver=%(dns)s --onboot=True'
                %{'ip':ip,'sysname':sysname,'mac':mac,'gate':gateway,'dns':nservers},file=fd)
    for (k,v) in interfaces.items():
        print('network --bootproto=dhcp --device=%s --bindto=mac --onboot=False --nodefroute --notksdevice'%(k,),file=fd)
#
%end

This script handles all of the tasks associated with assigning a network address and hostname in a kickstart file, provided you have a mapping of MAC address to IP to hostname. As this was intended to be part of a kickstart, I did not try to store the mappings in a file to read in, but instead had them in a couple of large string variables.

It is possible to assign multiple interfaces on a single node with this script, as long as they all reference the same hostname.

As a followup to my previous post, I did do the EFI boot, and a similar message was posted. After reading through the logs, I am even more puzzled as to what causes the problem, since the same error message is given during the successful EFI install.

It would be interesting to check if a network boot to install has similar behavior.

Thanks for the detailed information.
I also found the script in one of you other posts.
It took a while before i realized it was python and not shell script.