Skip to content

Commit

Permalink
custompartition.py: support xfs and btrfs filesystem.
Browse files Browse the repository at this point in the history
The change helps to add support for xfs and btrfs filesystem.
In order to create xfs or btrfs filesystem, "filesystem" value needs
to be set to "xfs" or "btrfs" in kickstart.

Ex - {"mountpoint": "/", "size": 2048, "filesystem": "btrfs"}
Ex - {"mountpoint": "/", "size": 2048, "filesystem": "xfs"}

{"mountpoint": "/", "size": 2048, "filesystem": "btrfs", "btrfs":
{"label": "main"}}

"btrfs" key in above example is optional and helps create parent volume
label.

Creating btrfs subvolumes-

In order to create subvolume for btrfs "subvols" key needs to
be defined inside "btrfs" dictionary.

"subvols" is a dictionary containing subvolume name and two optional
keys "mountpoint" and "subvols". In order to create nested subvolumes
"subvols" needs to be defined in similar manner as parent subvol inside
"subvols" key.

Ex -

{ "partitions" : [
      { "mountpoint": "/", "size": 2048, "filesystem": "btrfs",
        "btrfs" :
		{
                    "label" : "main",
                    "subvols" :
			[
				{"name": "rootfs", "mountpoint": "/root"},
				{"name": "Users"},
				{"name": "home", "mountpoint": "/home", "subvols": [{"name": "dir1", "mountpoint": "/dir1"}]}
			]
         	}
      }
   ]
}

Change-Id: Ie1459b22238e5290e0558bd4bca87dc329b1827d
  • Loading branch information
gpiyush-dev committed Nov 21, 2022
1 parent 52c657e commit 8a3c0c0
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 5 deletions.
4 changes: 2 additions & 2 deletions photon_installer/custompartition.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def validate_partition(self, pstr):
else:
return False, "Input cannot be empty"

if typedata != 'swap' and typedata != 'ext3' and typedata != 'ext4':
if typedata not in ['swap', 'ext3', 'ext4', 'xfs', 'btrfs']:
return False, "Invalid type"

if len(mtdata) != 0 and mtdata[0] != '/':
Expand Down Expand Up @@ -151,7 +151,7 @@ def create_function(self):
self.partition_items.append(('Size in MB: ' +
str(self.disk_size[self.device_index][1]) +
' available'))
self.partition_items.append(('Type: (ext3, ext4, swap)'))
self.partition_items.append(('Type: (ext3, ext4, xfs, btrfs, swap)'))
self.partition_items.append(('Mountpoint:'))
self.create_window = ReadMulText(
self.maxy, self.maxx, 0,
Expand Down
80 changes: 78 additions & 2 deletions photon_installer/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,17 @@ def _get_uuid(self, path):
return subprocess.check_output(['blkid', '-s', 'UUID', '-o', 'value', path],
universal_newlines=True).rstrip('\n')

def _add_btrfs_subvolume_to_fstab(self, mnt_src, fstab_file, btrfs_partition, parent_subvol=''):
"""
Recursive function to add btrfs subvolume and nested subvolumes to fstab
fstab entry ex - UUID=0b56138b-6124-4ec4-a7a3-7c503516a65c /data/projects btrfs subvol=projects 0 0
"""
for subvol in btrfs_partition["subvols"]:
if "mountpoint" in subvol:
fstab_file.write(f"{mnt_src}\t{subvol['mountpoint']}\tbtrfs\tsubvol="+ os.path.join(parent_subvol, subvol['name']) +"\t0\t0\n")
if "subvols" in subvol:
self._add_btrfs_subvolume_to_fstab(mnt_src, fstab_file, subvol, os.path.join(parent_subvol, subvol['name']))

def _create_fstab(self, fstab_path=None):
"""
update fstab
Expand All @@ -596,8 +607,11 @@ def _create_fstab(self, fstab_path=None):
dump = 1
fsck = 2

if partition.get('mountpoint', '') == '/':
options = options + ',barrier,noatime,noacl,data=ordered'
# Add supported options according to partition filesystem
if partition.get('mountpoint', '') == '/' and partition.get('filesystem','') != 'xfs':

This comment has been minimized.

Copy link
@YustasSwamp

YustasSwamp Dec 15, 2022

please make this code more readable and error prone if you add more partition types. Example:
if ext4, do this
else if btrfs, do that
else if xfs do nothing
else throw an error: implement me

options = options + ',barrier,noatime,noacl'
if partition.get('filesystem','') != 'btrfs':
options += ',data=ordered'
fsck = 1

if ptype == PartitionType.SWAP:
Expand Down Expand Up @@ -630,6 +644,10 @@ def _create_fstab(self, fstab_path=None):
dump,
fsck
))

if partition.get('filesystem', '') == "btrfs" and "btrfs" in partition and "subvols" in partition["btrfs"]:
self._add_btrfs_subvolume_to_fstab(mnt_src, fstab_file, partition["btrfs"])

# Add the cdrom entry
fstab_file.write("/dev/cdrom\t/mnt/cdrom\tiso9660\tro,noauto\t0\t0\n")

Expand Down Expand Up @@ -663,6 +681,11 @@ def _mount_partitions(self):
if retval != 0:
self.logger.error("Failed to mount partition {}".format(partition["path"]))
self.exit_gracefully()
if partition['filesystem'] == "btrfs" and "btrfs" in partition:
if "label" in partition["btrfs"]:
self.cmd.run(f"btrfs filesystem label {mountpoint} {partition['btrfs']['label']}")
if "subvols" in partition["btrfs"]:
self._create_btrfs_subvolumes(mountpoint, partition["btrfs"], partition["path"])

def _initialize_system(self):
"""
Expand Down Expand Up @@ -1069,6 +1092,50 @@ def _partition_type_to_string(self, ptype):
return '8300'
raise Exception("Unknown partition type: {}".format(ptype))

def _mount_btrfs_subvol(self, mountpoint, disk, subvol_name, fs_options=None, parent_subvol=""):
"""
Mount btrfs subvolume if mountpoint specified.
Create mountpoint directory inside given photon root.
If nested subvolume then append parent subvolume to identify the given subvolume to mount.
If fs_options provided then append fs_options to given mount options.
"""
self.logger.info(self.photon_root + mountpoint)
mountpt = self.photon_root + mountpoint
self.cmd.run(["mkdir", "-p", mountpt])
mount_cmd = ['mount', '-v', disk]
options = "subvol=" + os.path.join(parent_subvol, subvol_name)
if fs_options:
options += f",{fs_options}"
mount_cmd.extend(['-o', options, mountpt])
retval = self.cmd.run(mount_cmd)
if retval:
self.logger.error(f"Failed to mount subvolume {parent_subvol}/{subvol_name} to {mountpt}")
self.exit_gracefully()

def _create_btrfs_subvolumes(self, path, partition, disk, parent_subvol=""):
"""
Recursive function to create btrfs subvolumes.
Iterate over list of subvols in a given btrfs partition.
If "mountpoint" exists inside subvolume mount the subvolume at given mountpoint.
Label the subvolume if "label" exists in subvolume.
Create nested subvolume if "subvols" key exists inside parent subvolume.
"""
for subvol in partition["subvols"]:
if subvol.get("name") is None:
self.logger.error("Failed to get subvol 'name'")
self.exit_gracefully()
retval = self.cmd.run(["btrfs", "subvolume", "create", os.path.join(path, subvol["name"])])
if retval:
self.logger.error(f"Error: Failed to create subvolume {path}")
self.exit_gracefully()
if "mountpoint" in subvol:
self._mount_btrfs_subvol(subvol["mountpoint"], disk, subvol["name"], subvol.get("fs_options", None), parent_subvol)
if "label" in subvol:
self.cmd.run(f"btrfs filesystem label " + os.path.join(path, subvol['name']) + f" {subvol['label']}")
if "subvols" in subvol:
self._create_btrfs_subvolumes(os.path.join(path, subvol["name"]), subvol, disk, os.path.join(parent_subvol, subvol["name"]))

def _create_logical_volumes(self, physical_partition, vg_name, lv_partitions, extensible):
"""
Create logical volumes
Expand Down Expand Up @@ -1316,6 +1383,11 @@ def _partition_disk(self):
elif partition['mountpoint'] == '/boot':
partitions_data['boot'] = partition['path']
partitions_data['bootdirectory'] = '/'
if "filesystem" in partition:

This comment has been minimized.

Copy link
@YustasSwamp

YustasSwamp Dec 15, 2022

Are these packages required at runtime to mount these partitions? Probably not, then

Installed should not force these packages to be installed. Even though, these packages can not be available in custom ISO.

From other side these package must be installed in installer initrd.

Please clean it up.

if partition["filesystem"] == "xfs":
self._add_packages_to_install('xfsprogs')
elif partition["filesystem"] == "btrfs":
self._add_packages_to_install('btrfs-progs')

# If no separate boot partition, then use /boot folder from root partition
if 'boot' not in partitions_data:
Expand Down Expand Up @@ -1343,6 +1415,10 @@ def _format_partitions(self):
else:
mkfs_cmd = ['mkfs', '-t', partition['filesystem']]

# Add force option to mkfs to override previously created partition

This comment has been minimized.

Copy link
@YustasSwamp

YustasSwamp Dec 15, 2022

is it not the case for ext4?
Is it really required?

if partition["filesystem"] in ["btrfs", "xfs"]:
mkfs_cmd.extend(['-f'])

if 'mkfs_options' in partition:
options = re.sub(r"[^\S]", " ", partition['mkfs_options']).split()
mkfs_cmd.extend(options)
Expand Down
26 changes: 25 additions & 1 deletion photon_installer/ks_config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Kickstart config file is a json format with following possible parameters:
Contains list of partitions to create.
Each partition is a dictionary of the following items:
"filesystem" (required)
Filesystem type. Supported values are: "swap", "ext4", "vfat".
Filesystem type. Supported values are: "swap", "ext4", "vfat", "xfs", "btrfs".
"disk" (optional if single disk device is available,
required if multiple disk devices are available)
Target disk device will have the defined partition
Expand All @@ -142,6 +142,30 @@ Kickstart config file is a json format with following possible parameters:
"fs_options" (optional)
fs options to be passed to mount command as a string
ex - "fs_options": "nodev,noexec,nosuid"
"btrfs" (optional)
Creates btrfs volumes and subvolumes.
Value is a dictionary with 1 required and 1 optional key.
"label" (optional)
Name of the parent volume label.
"subvols" (optional)
Subvolumes inside parent volume.
Ex -
"disk": "/dev/sda",
"partitions": [
{"mountpoint": "/", "size": 0, "filesystem": "btrfs"}]
Ex to create subvols -
{ "partitions" :
[{ "mountpoint": "/", "size": 2048, "filesystem": "btrfs",
"btrfs" :
{
"label" : "main",
"subvols" : [
{"name": "rootfs", "mountpoint": "/root"},
{"name": "home", "mountpoint": "/home", "subvols": [{"name": "dir1", "mountpoint": "/dir1"}]}
]
}
}]
}
"lvm" (optional)
Will logical volume (LVM) for this partition.
Value is a dictionary with 2 required keys:
Expand Down

1 comment on commit 8a3c0c0

@YustasSwamp
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @gpiyush-dev, please see my comments above

Please sign in to comment.