This is my Partition table template:
#!/usr/bin/python
#Dynamic
# ------------------------------------------------------------------------------
# RHEL Version 7.x Kickstart script to probe for storage devices (hard drives,
# LUNs, etc.) and create Kickstart bootloader, clearpart, partition, and logical
# volume options in the file /tmp/diskpart.cfg. Kickstart variables may be used to
# control script behavior
# Kickstart variables: (sizes ae in MB)
# rootMax - maximum size of / file system. Default: 16384
# tmpMax - maximum size of /tmp file system. Default: 4096
# varMax - maximum size of /var file system. Default: 4096
# logMax - maximum size of /var/log file system. Default: 4096
# optMax - maximum size of /var/log file system. Default: 4096
# auditMax - maximum size of /var/log/audit file system. Default: 2048
# homeMax - maximum size of /home file system. Default: Default: 4096
# rootPercent - percent of boot drive to use for / file system. Default: 25% (0.25)
# tmpPercent - percent of boot drive to use for /tmp file system. Default: 10% (0.1)
# varPercent - percent of boot drive to use for /var file system. Default: 10% (0.1)
# logPercent - percent of boot drive to use for /var/log file system. Default: 10% (0.1)
# optPercent - percent of boot drive to use for /var/opt file system. Default: 10% (0.1)
# auditPercent - percent of boot drive to use for /var/log/audit file system. Default: 5% (0.05)
# homePercent - percent of boot drive to use for /home file system. Default: 10% (0.1)
# opt - flag (True|False) to create the /opt file system. Default: True
# lastBiosDisk - highest BIOS Int13 Disk ID (0x80, 0x81 ...) to include in
# the configuration, all additional BIOS drives will be
# treated as "otherDisks". Default: 0x8f
# otherDisks - Python dictionary like list of drive models and number
# of each model of drive to include in configuration
# (i.e. 'Dell PERC 6/i': 2, 'HITACHI OPEN-V': 4, 'NETAPP LUN': 0, 'HP HSV400': 0, 'VMware Virtual disk': 0)
# Default: {} (empty dictionary)
# lvCount - Maximum number of optional Logical Volumes to create. Default: 16
# lvName# - Name of optional LV to create (where # is integer up to lvCount)
# lvMount# - Mount point for optional LV (where # is integer up to lvCount) Default: '/'
# lvGroup# - Preferred Voulme Group for optional LV (where # is integer up to lvCount) Default: 1 => datavg1
# lvSize# - LV Size in GB (where # is integer up to lvCount). Default: 1024
# lvFsType# - LV File System Type. Default: ext4
# Version 1.00 Released 05/10/2019
# ------------------------------------------------------------------------------
<%#
kind: ptable
name: Kickstart dynamic
model: Ptable
oses:
- CentOS
- Fedora
- RedHat
-%>
<%#
This template accepts the following parameters:
- fsType - file system type for default file systems. Default: xfs
- rootMax - maximum size of / file system. Default: 16384
- tmpMax - maximum size of /tmp file system. Default: 4096
- varMax - maximum size of /var file system. Default: 4096
- logMax - maximum size of /var/log file system. Default: 4096
- optMax - maximum size of /var/log file system. Default: 4096
- auditMax - maximum size of /var/log/audit file system. Default: 2048
- homeMax - maximum size of /home file system. Default: Default: 4096
- rootPercent - percent of boot drive to use for / file system. Default: 25% (0.25)
- tmpPercent - percent of boot drive to use for /tmp file system. Default: 10% (0.1)
- varPercent - percent of boot drive to use for /var file system. Default: 10% (0.1)
- logPercent - percent of boot drive to use for /var/log file system. Default: 10% (0.1)
- optPercent - percent of boot drive to use for /var/opt file system. Default: 10% (0.1)
- auditPercent - percent of boot drive to use for /var/log/audit file system. Default: 5% (0.05)
- homePercent - percent of boot drive to use for /home file system. Default: 10% (0.1)
- opt - flag (True|False) to create the /opt file system. Default: True
- lastBiosDisk - highest BIOS Int13 Disk ID (0x80, 0x81 ...) to include in
- the configuration, all additional BIOS drives will be
- treated as "otherDisks". Default: 0x8f
- otherDisks - Python dictionary like list of drive models and number
- of each model of drive to include in configuration
- (i.e. 'Dell PERC 6/i': 2, 'HITACHI OPEN-V': 4, 'NETAPP LUN': 0, 'HP HSV400': 0, 'VMware Virtual disk': 0)
- Default: {} (empty dictionary)
- lvCount - Maximum number of optional Logical Volumes to create. Default: 16
- lvName- - Name of optional LV to create (where # is integer up to lvCount)
- lvMount- - Mount point for optional LV (where # is integer up to lvCount) Default: '/'
- lvGroup- - Preferred Voulme Group for optional LV (where # is integer up to lvCount) Default: 1 => datavg1
- lvSize- - LV Size in GB (where # is integer up to lvCount). Default: 1024
- lvFsType- - LV File System Type. Default: ext4
-%>
#
# ------------------------------------------------------------------------------
# Set sizing rule values, defaults can be overridden with Host parameters
# ------------------------------------------------------------------------------
fsType = <%= host_param('fsType') || 'xfs' %>
rootMax = <%= host_param('rootMax') || '8192' %>
tmpMax = <%= host_param('tmpMax') || '4096' %>
varMax = <%= host_param('varMax') || '4096' %>
logMax = <%= host_param('logMax') || '4096' %>
optMax = <%= host_param('optMax') || '4096' %>
auditMax = <%= host_param('auditMax') || '2048' %>
homeMax = <%= host_param('homeMax') || '4096' %>
rootPercent = <%= host_param('rootPercent') || '0.25' %>
tmpPercent = <%= host_param('tmpPercent') || '0.1' %>
varPercent = <%= host_param('varPercent') || '0.1' %>
logPercent = <%= host_param('logPercent') || '0.1' %>
optPercent = <%= host_param('optPercent') || '0.1' %>
auditPercent = <%= host_param('auditPercent') || '0.05' %>
homePercent = <%= host_param('homePercent') || '0.1' %>
opt = <%= host_param('opt') || 'True' %>
lastBiosDisk = <%= host_param('lastBiosDisk') || '0x81' %>
otherDisks = {<%= host_param('otherDisks') || "'VMware Virtual disk': 4" %>}
optionalLVs = {}
<%
lvCount = host_param('lvCount') || '16'
(1..lvCount.to_i).each do |i|
iStr = i.to_s
-%>
<% if host_param('lvName' + iStr) -%>
optionalLVs ['<%= host_param('lvName' + iStr) -%>'] = ('<%= host_param('lvMount' + iStr).to_s.sub(/\/\z/, '') -%>', '<%= host_param('lvGroup' + iStr) || '1' -%>', <%= host_param('lvSize' + iStr) || '1024' -%>, '<%= host_param('lvFsType' + iStr) || 'xfs' -%>')
<% end -%>
<% end -%>
# ------------------------------------------------------------------------------
# Import libraries
# ------------------------------------------------------------------------------
import sys, os, re, parted # Import standard libraries
from itertools import repeat
import blivet
# ------------------------------------------------------------------------------
# Define local functions
# ------------------------------------------------------------------------------
def sortedDictValues(aDict): # Function to sort a dictionary by its keys
items = aDict.items() # Get the dictionarys items as a lit of tuples (key, value)
items.sort() # Sort the list
return [value for key, value in items] # Return a list of the values
# Function to return Subset of a Dictionary
dictSubset = lambda x, y: dict(zip(x, map(y.get, x)))
# ------------------------------------------------------------------------------
# Create empty dictionaries and lists
# ------------------------------------------------------------------------------
biosDisks = {} # Dictinary of BOIS Disk Names, by BIOS ID (0x80, 0x81, 0x82 ...)
allDiskModelsSizes = {} # Dictionary of Disk Models/Sizes by Device Name
mpathNames = {} # Dictionary of Multipath Devices by Member Name
mpathMemberNames = {} # Dictionary of Member Names by Multipath Device Name
lstOtherDisks = [] # List of non-BIOS Disks
lstMPathNames = [] # List of Multipath Device Names
lstMPathMemberNames = [] # List of Multipath Member Names
# ------------------------------------------------------------------------------
# Check if system is UEFI
# ------------------------------------------------------------------------------
if os.path.isdir('/sys/firmware/efi'):
uEFI = True
else:
uEFI = False
# ------------------------------------------------------------------------------
# Find Physical Memory size by analyzing the memory map
# ------------------------------------------------------------------------------
iomemFile = open('/proc/iomem', 'r') # Open /proc/meminfo read-only
for line in iomemFile: # For each line in the "file"
# Look for lines containing "System RAM"
if re.search('System RAM', line) is not None:
# Get the memory range from the line
memRange = tuple(map(int, line.split(':', 1)[0].split('-'), repeat(16, 2)))
iomemFile.close() # Close the file
# Convert the highest memory range to GB
sysMem = (memRange[1] + 1.0) / (1024 * 1024 * 1024)
if sysMem > 4: # If the result is greater than 4GB
sysMem = sysMem - 1 # Adjust for Extended Memory mapping
# ------------------------------------------------------------------------------
# Find the Single and Multipath disks
# ------------------------------------------------------------------------------
storageConfig = blivet.Blivet() # Create a storage configuration object
storageConfig.reset() # Read the current storage config
drives = storageConfig.disks # Get a list of all Storage Device objects
for drive in drives: # For each discovered drive
# Get the Vendor/Model string and Size
allDiskModelsSizes[drive.path[5:]] = (drive.vendor +
' ' +
drive.model,
int(drive.size.convertTo('MiB')))
if drive.type == 'dm-multipath': # If the drive is a Multipath device
lstMPathNames.append(drive.path[5:]) # Add the drive name to the list of Multipath devices
for parent in drive.parents: # For each parent of the Multipath device
# Add the parent name to the list of Multipath Member devices
lstMPathMemberNames.append(parent.name)
lstSPathNames = list(x.path[5:] for x # Get a list of non-Multipath Member drives
in drives
if x.type != 'dm-multipath'
and x.name not in lstMPathMemberNames)
lstAllDisks = lstSPathNames + lstMPathNames # Combine both lists
# ------------------------------------------------------------------------------
# Locate the 'BIOS' drives for compatibility with RHEL5/6
# ------------------------------------------------------------------------------
firstDisk = lstAllDisks[0] # Get the first disk from combined lists
# Get the Vendor/Model string and Size
firstDiskModel, firstDiskSize = allDiskModelsSizes[firstDisk]
for biosDev in range(0x80, lastBiosDisk + 1): # For BIOS drives 0x80 through lastBiosDisk
if biosDev - 0x80 < len(lstAllDisks): # If a matching entry exists
# Get its Vendor/Model string and Size
diskModel = allDiskModelsSizes[lstAllDisks[biosDev - 0x80]][0]
if diskModel == firstDiskModel: # If the Model is the same as the first drive
# Add to the list of 'BIOS' drives
biosDisks[biosDev] = lstAllDisks[biosDev - 0x80]
lstBiosDisks = sortedDictValues(biosDisks) # Convert the dictionary of BIOS Disks to a list sorted by their Int 0x13 ID
strBiosDisks = ','.join(lstBiosDisks) # Convert the list of BIOS Disks to a string
# ------------------------------------------------------------------------------
# Locate the 'Other' drives for compatibility with RHEL5/6
# ------------------------------------------------------------------------------
lstOtherDisks = list(x for x # Get a list of any other drives
in lstAllDisks
if x not in lstBiosDisks)
# ------------------------------------------------------------------------------
# Get information for the boot drive
# ------------------------------------------------------------------------------
# Find the "Size" of the boot Disk
bootDiskModel, bootDiskSize = allDiskModelsSizes[biosDisks[0x80]]
clearDrives = biosDisks[0x80] # Clear partitions on the boot Disk
# ------------------------------------------------------------------------------
# Build Dictionaries of lists BIOS and other Disks with Disk Model/Size as the keys
# ------------------------------------------------------------------------------
# Build a dictionary with each BIOS disk model as the key
# and a list of the corresponding devices as the values
biosDiskModelsSizes = dict((v, list(key for key, value in allDiskModelsSizes.items() if value == v and key in lstBiosDisks[1:])) for v in set(dictSubset(lstBiosDisks[1:], allDiskModelsSizes).values()))
# Build a dictionary with each non-BIOS disk model as the key
# and a list of the corresponding devices as the values
otherDiskModelsSizes = dict((v, list(key for key, value in allDiskModelsSizes.items() if value == v and key in lstOtherDisks)) for v in set(dictSubset(lstOtherDisks, allDiskModelsSizes).values()))
# ------------------------------------------------------------------------------
# Open a file (/tmp/diskpart.cfg) to save all Partitioning options
# ------------------------------------------------------------------------------
myFile = open('/tmp/diskpart.cfg', 'w+') # Save all output in /tmp/diskpart.cfg
myFile.write('# Memory Size = %s GB\n' % sysMem)
# ------------------------------------------------------------------------------
# Create the bootloader option
# ------------------------------------------------------------------------------
myFile.write('bootloader --location=mbr --driveorder=%s --append="crashkernel=auto"\n' % lstBiosDisks[0])
# ------------------------------------------------------------------------------
# Create the boot disk options
# ------------------------------------------------------------------------------
# Add a comment with the boot disk model
myFile.write('# Boot Disk Model: %s - %d GB\n' % (bootDiskModel, bootDiskSize/1024))
# Create the /boot partition option
bootPartSize = 512
myFile.write('part /boot --fstype=ext4 --size=%s --ondisk=%s --asprimary\n' % (str(bootPartSize), lstBiosDisks[0]))
if uEFI:
myFile.write('part /boot/efi --fstype=efi --size=%s --ondisk=%s --label=EFI --asprimary\n' % (str(bootPartSize), lstBiosDisks[0]))
# Create a partition option for the rest of the boot drive
myFile.write('part pv.1000 --size=1 --grow --ondisk=%s --asprimary\n' % lstBiosDisks[0])
myFile.write('volgroup rootvg pv.1000\n') # Create the rootvg VG using the boot drive only
volumeGroups = {0: 'rootvg'} # Create an indexed list of volume groups
# ------------------------------------------------------------------------------
# Build partition options for additional BIOS disks, grouped by disk model
# ------------------------------------------------------------------------------
dataVgCount = 0 # Volume Group Counter
pvCount = 0 # Physical Volume Counter
# For each model/size (sorted by model/size)
for model, size in sorted(biosDiskModelsSizes.iterkeys()): # For each model/size (sorted by model)
drives = biosDiskModelsSizes[(model, size)] # Get the list of drives for this model/size
dataVgCount = dataVgCount + 1 # Increment the Data VG Count
lstPartitions = [] # Start with an empty list of partitions
drives.sort() # Sort the drives
# Add a comment with the disk model/size
myFile.write('# Disk Model: %s - %d GB\n' % (model, size/1024))
pvCount = pvCount + 1 # Increment the Physical Volume counter
for drive in drives: # For each drive in the drive list
# Create a partiton Option
myFile.write('part pv.%d%03d --size=1 --grow --ondisk=%s --asprimary\n' % (pvCount, drives.index(drive) + 1, drive))
clearDrives = clearDrives + ',' + drive # Add the drive to the string of drives to have their partitions cleared
# Add the partition to a list of partitions in this VG
lstPartitions.append('pv.%d%03d' % (pvCount, drives.index(drive) + 1))
# Create a data VG option using all the disks of this model
myFile.write('volgroup datavg%d %s\n' % (dataVgCount, ' '.join(lstPartitions)))
volumeGroups[dataVgCount] = 'datavg' + str(dataVgCount)
# ------------------------------------------------------------------------------
# Build partition options for non-BIOS disks, grouped by disk model
# ------------------------------------------------------------------------------
if len(otherDisks) > 0: # If non-BIOS Disks Exist
# For each model/size and drive in the list of other drives
for model, size in sorted(otherDiskModelsSizes.iterkeys()):
# Get the list of drives for this model/size
drives = otherDiskModelsSizes[(model, size)]
# If this model is in the dictionary of models to configure
if model in otherDisks.keys() and otherDisks[model] > 0:
dataVgCount = dataVgCount + 1 # Increment the Data VG Count
lstPartitions = [] # Start with an empty list of partitions
drives.sort() # Sort the drives
# Add a comment with the disk model/size
myFile.write('# Disk Model: %s - %d GB\n' % (model, size/1024))
# For each drive in the drive list, up to the specified number
pvCount = pvCount + 1 # Increment the Physical Volume counter
for drive in drives[0:otherDisks[model]]:
# Create a partiton option
myFile.write('part pv.%d%03d --size=1 --grow --ondisk=%s --asprimary\n' % (pvCount, drives.index(drive) + 1, drive))
# Add the drive to the string of drives to have their partitions cleared
clearDrives = clearDrives + ',' + drive
# Add the partition to a list of partitions in this VG
lstPartitions.append('pv.%d%03d' % (pvCount, drives.index(drive) + 1))
# Create a data VG option using all the disks of this model
myFile.write('volgroup datavg%d %s\n' % (dataVgCount, ' '.join(lstPartitions)))
volumeGroups[dataVgCount] = 'datavg' + str(dataVgCount)
else: # If model not in dictionary of models to be configured
# Write a comment listing model skipped
myFile.write('# Skipping Disk Model: %s - %10.4f\n' % (model, size))
# ------------------------------------------------------------------------------
# Create Clear Partition Option including all selected disks
# ------------------------------------------------------------------------------
if uEFI:
myFile.write('clearpart --none\n') # Do not clear partitions on EFI Disks during Kickstart
from subprocess import call, Popen, PIPE # Import subprocess functions
from stat import S_ISBLK # Import the Is Block Device value
for drive in clearDrives.split(','): # For each Drive to be cleared
drivePath = '/dev/' + drive # Get the device path
mode = os.stat(drivePath).st_mode # Get the mode of the path
if S_ISBLK(mode): # If the path is a Block Special Device
# Zero the MBR and create a GUID Partition Table
call(['/usr/bin/dd', 'if=/dev/zero', 'of=' + drivePath, 'bs=512', 'count=256'])
call(['/usr/sbin/parted', '--script', drivePath, 'mklabel', 'gpt'])
else:
# Create the BIOS clear partiton option
myFile.write('clearpart --drives=%s --all --initlabel\n' % clearDrives)
# ------------------------------------------------------------------------------
# Calculate size for swap
# ------------------------------------------------------------------------------
if sysMem <= 2:
swapSize = int(sysMem * 1.5 * 1024)
elif 2 < sysMem <= 16:
swapSize = int(sysMem * 1024)
else:
swapSize = 16384
# Write a comment listing model skipped
bootDiskSize = bootDiskSize - swapSize # Deduct swapSize from bootDiskSize
bootDiskSize = bootDiskSize - bootPartSize # Deduct bootPartSize from bootDiskSize
if uEFI: # I this is an EFI system
bootDiskSize = bootDiskSize - bootPartSize # Deduct EFI partition size
# ------------------------------------------------------------------------------
# calculate sizes for rootvg logical volumes
# ------------------------------------------------------------------------------
rootSize = int(min(rootMax, bootDiskSize * rootPercent))
tmpSize = int(min(tmpMax, bootDiskSize * tmpPercent))
varSize = int(min(varMax, bootDiskSize * varPercent))
logSize = int(min(logMax, bootDiskSize * logPercent))
optSize = int(min(optMax, bootDiskSize * optPercent))
auditSize = int(min(auditMax, bootDiskSize * auditPercent))
homeSize = int(min(homeMax, bootDiskSize * homePercent))
# ------------------------------------------------------------------------------
# Write rootvg Logical Volume options
# ------------------------------------------------------------------------------
myFile.write('logvol / --fstype=%s --name=root --vgname=rootvg --size=%d --fsoptions="defaults,noatime"\n' % (fsType, rootSize))
myFile.write('logvol swap --name=swap --vgname=rootvg --size=%d\n' % swapSize)
myFile.write('logvol /tmp --fstype=%s --name=tmp --vgname=rootvg --size=%d --fsoptions="defaults,noatime,nodev,nosuid,noexec"\n' % (fsType, tmpSize))
myFile.write('logvol /var --fstype=%s --name=var --vgname=rootvg --size=%d --fsoptions="defaults,noatime,nodev"\n' % (fsType, varSize))
myFile.write('logvol /var/log --fstype=%s --name=log --vgname=rootvg --size=%d --fsoptions="defaults,noatime,nodev"\n' % (fsType, logSize))
if opt:
myFile.write('logvol /opt --fstype=%s --name=opt --vgname=rootvg --size=%d --fsoptions="defaults,noatime,nodev"\n' % (fsType, optSize))
myFile.write('logvol /var/log/audit --fstype=%s --name=audit --vgname=rootvg --size=%d --fsoptions="defaults,noatime,nodev"\n' % (fsType, auditSize))
myFile.write('logvol /home --fstype=%s --name=home --vgname=rootvg --size=%d --fsoptions="defaults,noatime,nodev"\n' % (fsType, homeSize))
# ------------------------------------------------------------------------------
# Build optional logical volumes
# ------------------------------------------------------------------------------
for lvName, (lvMount, lvGroup, lvSize, lvFsType) in optionalLVs.iteritems():
myFile.write('logvol %s --fstype=%s --name=%s --vgname=%s --size=%d --fsoptions="defaults,noatime,nodev"\n' % (lvMount + '/' + lvName,
lvFsType,
lvName,
volumeGroups[min(lvGroup, dataVgCount)],
lvSize))
# ------------------------------------------------------------------------------
# Close file and exit
# ------------------------------------------------------------------------------
myFile.close()
sys.exit()