I have used kickstart to set up numerous systems with AlmaLinux 8 and 9. I have created a script that runs as a pre-install that determines the disk layout predictably on whatever the hardware happens to be. The script also sets up the network IP from the MAC address, as we use static IP for our systems. I raided the arp cache on a few of the current machines to get a mapping of MAC to IP and MAC to hostname, which I have left a sample of in the Kickstart to make the format make sense. I set this up as we were implementing IdM on this domain, and have it installed as part of the kickstart.
The key to the partitioning is the names that exist in /dev/disk. There are 6 sub-directories that map the disks with symbolic links to the /dev names for the devices. The ones that I use are the by-label and by-id to find the installation source, and then to filter out USB devices and other smaller devices from consideration for the install. I then set the swap partition size, and then start the setup of partitions, writing the info out to a file in /tmp. This file is then read by Anaconda and the disks are then set up on the fly. I am attaching a sanitized version of the kickstart that yo are free to use if you like.
Enjoy.
#version=RHEL8
# vi:set ts=4 et:
#
eula --agreed
# Use graphical install
graphical
# Network information
%include /tmp/net.cfg
#
#network --bootproto=dhcp --device=link --ipv6=auto --activate
#network --bootproto=dhcp --hostname=alma.ipa.localdomain
#
%include /tmp/parts.cfg
# Installation sources -- Moved to pre script include
harddrive --partition=LABEL=ALMALINUX-8 --dir=
repo --name="AppStream" --baseurl=file:///run/install/repo/AppStream
#
# Keyboard layouts
keyboard --xlayouts='us'
# System language
lang en_US.UTF-8
module --name idm --stream DL1 # idm servers,client
%packages
@^graphical-server-environment
@development
@graphical-admin-tools
@network-file-system-client
@system-tools
@idm:DL1/client
#@idm:DL1/server
#@idm:DL1/dns
kexec-tools
gnome-tweaks
python3-pip
vim-X11
-insights-client
-cockpit
%end
# Run the Setup Agent on first boot
firstboot --disable
# System timezone
timezone America/Chicago --isUtc --ntpservers=2.cloudlinux.pool.ntp.org,2.cloudlinux.pool.ntp.org,2.cloudlinux.pool.ntp.org,2.cloudlinux.pool.ntp.org,time.google.com,time.google.com,time.google.com,time.google.com
# Root password
rootpw --iscrypted <removed encrypted password>
reboot
%addon com_redhat_kdump --enable --reserve-mb='auto'
%end
%anaconda
pwpolicy root --minlen=9 --minquality=30 --strict --nochanges --notempty
pwpolicy user --minlen=9 --minquality=30 --strict --changesok --notempty
pwpolicy luks --minlen=9 --minquality=30 --strict --nochanges --notempty
%end
##
%pre --interpreter=/usr/libexec/platform-python
#
import subprocess
cache='''
00:21:9f:73:f9:83 10.0.0.162
7c:5c:f8:ff:3f:20 10.0.0.219
00:1b:21:bb:ff:94 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:83 test1.ipa.localdomain
7c:5c:f8:f0:3f:20 test2.ipa.localdomain
00:1b:21:bb:71:94 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 = 'xxx.xxx.xxx.xxx'
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)
#
# Autopartition Script
#
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 = {}
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:
continue
if sizes[rp.name] < 200:
continue
targets[rp.name] = rp
if DEBUG:
print('\n-------------------------------------------------------------\n')
print(targets)
#
# 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
'''
#
##
fd = sys.stdout
print('ignoredisk --drives=%s'%(sourceDevice),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 = open('/tmp/parts.cfg',mode='w')
print('ignoredisk --drives=%s'%(sourceDevice),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