How to modify default storage layouts (i.e. LVM)

Hi everyone, I’m new to MaaS and I’m trying to deploy machines with custom storage layout. As I’ve read in the documentation I should choose “Custom storage layout” in settings and provide commissioning script, which will configure storage. But is there any way to add or modify default storage layout (Flat, LVM…), which would be visible in Settings?
Best regards
Adam

Not easily. Any upgrade to MaaS will likely wipe out whatever changes you make. But you can add up to 9 custom scripts to accomplish most things. The defaults are all based on a single drive configuration. If you have multiple drives then the custom script is the better option.

Hi, thanks for the answer. Actually most often I have 2x nvme with EFI partition on each drive and lvm on raid composed from the second partitions. Is there any recommendation what kind of script should it be? Bash? Perl? Python?

It’s a python script inside a bash wrapper.
In a nutshell it takes the output of the 50-maas-01-commissioning script and modifies the detected drive/partition configuration and then pushes that back to the commissioning script.
Here is an example that I modified for my own use from Charles Bedfords Dell deployment. the program name is what shows up in the scripts menu. Make sure to save it as a *.sh file in order to upload it.

#!/usr/bin/env python3
####
#
#  Program: 42-custom-storage_host-sda-b_ceph-sdc-d
#
#  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-storage_host-sda-b_ceph-sdc-d
# title: Set layout for SuperMicro Node disks
# description: Set layout for SuperMicro Node disks
# script_type: commissioning
# timeout: 60
# --- End MAAS 1.0 script metadata ---
# tags: noauto
####
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":{
      "BDISK":{
         "type":"disk",
         "ptable":"gpt",
         "boot":true,
         "partitions":[
            {
               "name":"sda1",
               "fs":"fat32",
               "size":"2G",
               "bootable":true
            },
            {
               "name":"sda2",
               "size":"MAX",
               "fs":"ext4"
            }
         ]
      },
      "vg":{
         "type":"lvm",
         "members":[
            "sdb"
         ],
         "volumes":[
            {
               "name":"lv1",
               "size":"20G",
               "fs":"btrfs"
            },
            {
               "name":"lv2",
               "size":"16G",
               "fs":"swap"
            },
            {
               "name":"lv3",
               "size":"1T",
               "fs":"btrfs"
            },
            {
               "name":"lv4",
               "size":"1T",
               "fs":"btrfs"
            }
         ]
      }
   },
   "mounts":{
      "/":{
         "device":"sda2"
      },
      "/boot/efi":{
         "device":"sda1"
      },
      "none":{
         "device":"lv2"
      },
      "/var":{
         "device":"lv3"
      },
      "/tmp":{
         "device":"lv1"
      },
      "/home":{
         "device":"lv4"
      }
   }
}'''

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"]["BDISK"]


# Now delete the DISK temporary structure
del layoutDetail["layout"]["BDISK"]

####
#
#  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

####
#
#  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)

@bkslash, did @dracozny’s suggestions answer the problem? if not, what remains to be resolved?

Trying this as a direct cut and paste into my MAAS and the 50-maas-01-commissioning is failing now. I’m guessing there’s something possibly incorrect in the json output. There’s nothing showing up configured in the storage on the node either.

Hey Adam,

I’m gonna take one more swing for the fence, here.

When deploying machines with a custom storage layout in MAAS, modifying the default storage layouts (such as Flat or LVM) directly from the Settings is tricky business. You’re probably better off to use custom commissioning scripts to accomplish this, as hinted here.

You might try a few things:

  1. Custom commissioning script: As you mentioned, selecting “Custom storage layout” in the Settings and providing a commissioning script is the recommended approach. You can create a commissioning script that configures the storage layout according to your requirements. The script can be written in bash, Python, or any other scripting language supported by MAAS.
  2. Python script inside a bash wrapper: You can use a Python script inside a bash wrapper to modify the default storage layout. The Python script can read the current hardware configuration from the MAAS_RESOURCES_FILE and modify the storage layout accordingly. The example provided by @dracozny in the previous reply demonstrates this approach, but I’m underlining it as possibly a very good option.
  3. Customization based on drive configuration: If you have specific drive configurations, such as multiple NVMe drives with EFI partitions and LVM on a RAID, you can customize your commissioning script to handle this configuration. You can utilize the information from the MAAS_RESOURCES_FILE to detect the drives, partitions, and logical volumes and define the desired storage layout. It’s work, but it might work.

If you encounter issues with the commissioning scripts not functioning as expected or the storage configuration not being applied correctly, don’t forget to examine the logs and error messages to identify any possible errors. It goes without saying that you’ll probably have to debug the script, or at least make sure that it’s properly tucked into the commissioning process.

It’s worth mentioning that any modifications made to the default storage layouts or commissioning scripts may be overwritten during MAAS upgrades. Therefore, it’s really important to keep a backup of your custom scripts and configurations to reapply them if necessary.

If you need further assistance or encounter specific issues during the customization process, bounce back and we’ll take another swing at this. :slight_smile: