After a few trials and tribulations, I have a well commented script that I think people might find useful. It adheres to a number of standards which our shop uses, and leaves a chunk of disk unused for whatever the purpose of the system may call for.
Here’s what I ended up with:
#!/usr/bin/env python3 #### # # Program: 42-custom-disk-layout.sh # # Description: # This python script is to specify how to select and partition a disk in MAAS. # Inputs: the MAAS_RESOURCES_FILE from previous default scripts is a json # construct detailing the hardware that is currently being "commissioned". # Outputs: the MAAS_STORAGE_CONFIG_FILE should contain a json structure # that defines the selected hard disk and where to slice it up to the standard # mountpoints for the OS installation, however that step continually failed. # Instead this script loads all the data from the MAAS_RESOURCES_FILE, then # adds the ['storage-extra'] block with the selected disk, partitions, and # logical volumes. # # Programmer: Charles Bedford # History: # 2022-09-30 - CHB - Initial revision # # --- Start MAAS 1.0 script metadata --- # name: 42-custom-disk-layout # title: Set layout for DELL Virtual disks # description: Set layout for DELL Virtual disks # script_type: commissioning # timeout: 60 # --- End MAAS 1.0 script metadata --- # #### import json import os import sys # Function definitions def read_json_file(path): try: with open(path) as fd: return json.load(fd) except OSError as e: sys.exit(f"Failed to read {path}: {e}") except json.JSONDecodeError as e: sys.exit(f"Failed to parse {path}: {e}") # Load the hardware from the json in the MAAS_RESOURCES_FILE hardware = read_json_file(os.environ['MAAS_RESOURCES_FILE']) #### # # This is the primary datastructure (in json format) with # placeholders which are easily found and replaced below (DISK & MAX) # #### cfg = '''{ "layout": { "DISK": { "type": "disk", "ptable": "gpt", "boot": true, "partitions": [ { "name": "DISK1", "fs": "fat32", "size": "2G", "bootable": true }, { "name": "DISK2", "size": "MAX" } ] }, "vg": { "type": "lvm", "members": [ "DISK2" ], "volumes": [ { "name": "lv1", "size": "50G", "fs": "ext4" }, { "name": "lv2", "size": "16G", "fs": "swap" }, { "name": "lv3", "size": "20G", "fs": "ext4" }, { "name": "lv4", "size": "20G", "fs": "ext4" }, { "name": "lv5", "size": "20G", "fs": "ext4" }, { "name": "lv6", "size": "10G", "fs": "ext4" }, { "name": "lv7", "size": "20G", "fs": "ext4" }, { "name": "lv8", "size": "20G", "fs": "ext4" } ] } }, "mounts": { "/": { "device": "lv1" }, "/boot/efi": { "device": "DISK1" }, "none": { "device": "lv2" }, "/tmp": { "device": "lv3" }, "/var": { "device": "lv4" }, "/var/log": { "device": "lv5" }, "/var/log/audit": { "device": "lv6" }, "/var/tmp": { "device": "lv7" }, "/home": { "device": "lv8" } } }''' disks = hardware['resources']['storage']['disks'] # Initialize a few variables so we have consistent values below primary = '' primaryId = 0 diskSize = 0 index=0 # Iterate through all the disks defined for this machine for disk in disks: # skip virtual mounted drives from maas if 'Virtual' in disk["model"] or 'Virtual' in disk['device_id']: continue # find the PERC or DELLBOSS id to use... if 'PERC' in disk["model"] or 'DELLBOSS' in disk["model"]: primary = disk['id'] primaryId = index diskSize = disk['size'] index+=1 # if the loop didn't find a PERC or DELLBOSS drive to use # barring that it will have to be #0 if primary == '': primary=disks[0]['id'] primaryId = 0 diskSize = disks[0]['size'] # The disk ID (sda or whatever) that we want to use for booting... diskId = hardware["resources"]["storage"]["disks"][primaryId]["id"] # Load layoutDetail from the above structure layoutDetail = json.loads(cfg) # copy the DISK temporary structure over the top of the new diskId name layoutDetail["layout"][diskId] = layoutDetail["layout"]["DISK"] # Now delete the DISK temporary structure del layoutDetail["layout"]["DISK"] #### # # Cleanup the structure details # #### # set the names and sizes on the partitions: # Note: the sizes are NOT based on 1024, but 1000. # subtract the size of the fat32 partition and a constant... mySize = ( diskSize - 2000000000 ) / 1000000000 num = 1 for p in layoutDetail["layout"][diskId]["partitions"]: p["name"] = diskId + str(num) num += 1; if p["size"] == "MAX": p["size"] = str(int(mySize)) + "G" savedP = p # The Members of the lvm layoutDetail["layout"]["vg"]["members"] = [ savedP["name"] ] # last but not least - fix the one mount detail layoutDetail["mounts"]["/boot/efi"]["device"] = diskId + "1" #### # # Output # #### hardware["storage-extra"] = layoutDetail print('Saving custom storage layout to ' + os.environ['MAAS_RESOURCES_FILE']) print(json.dumps(hardware)) with open(os.environ['MAAS_RESOURCES_FILE'], 'w') as fd: json.dump(hardware, fd)
I hope that’s helpful to someone.
Thanks
~~ Charles