Merge pull request #355 from n3ph/master

Add mountopts and enable definition of additional partitions
This commit is contained in:
Anders Ingemann 2017-02-13 23:16:08 +01:00 committed by GitHub
commit a4e4ad9954
15 changed files with 165 additions and 22 deletions

View file

@ -41,7 +41,7 @@ class GPTPartitionMap(AbstractPartitionMap):
if 'boot' in data: if 'boot' in data:
self.boot = GPTPartition(Sectors(data['boot']['size'], sector_size), self.boot = GPTPartition(Sectors(data['boot']['size'], sector_size),
data['boot']['filesystem'], data['boot'].get('format_command', None), data['boot']['filesystem'], data['boot'].get('format_command', None),
'boot', last_partition()) data['boot'].get('mountopts', None), 'boot', last_partition())
if self.boot.previous is not None: if self.boot.previous is not None:
# No need to pad if this is the first partition # No need to pad if this is the first partition
self.boot.pad_start += partition_gap self.boot.pad_start += partition_gap
@ -57,12 +57,23 @@ class GPTPartitionMap(AbstractPartitionMap):
self.root = GPTPartition(Sectors(data['root']['size'], sector_size), self.root = GPTPartition(Sectors(data['root']['size'], sector_size),
data['root']['filesystem'], data['root'].get('format_command', None), data['root']['filesystem'], data['root'].get('format_command', None),
'root', last_partition()) data['root'].get('mountopts', None), 'root', last_partition())
if self.root.previous is not None: if self.root.previous is not None:
self.root.pad_start += partition_gap self.root.pad_start += partition_gap
self.root.size -= partition_gap self.root.size -= partition_gap
self.partitions.append(self.root) self.partitions.append(self.root)
# Create all additional partitions
for partition in data:
if partition not in ["boot", "swap", "root", "type"] and not None:
part_tmp = GPTPartition(Sectors(data[partition]['size'], sector_size),
data[partition]['filesystem'], data[partition].get('format_command', None),
data[partition].get('mountopts', None), partition, last_partition())
part_tmp.pad_start += partition_gap
part_tmp.size -= partition_gap
setattr(self, partition, part_tmp)
self.partitions.append(part_tmp)
if hasattr(self, 'grub_boot'): if hasattr(self, 'grub_boot'):
# Mark the grub partition as a bios_grub partition # Mark the grub partition as a bios_grub partition
self.grub_boot.flags.append('bios_grub') self.grub_boot.flags.append('bios_grub')

View file

@ -1,4 +1,5 @@
from abstract import AbstractPartitionMap from abstract import AbstractPartitionMap
from ..exceptions import PartitionError
from ..partitions.msdos import MSDOSPartition from ..partitions.msdos import MSDOSPartition
from ..partitions.msdos_swap import MSDOSSwapPartition from ..partitions.msdos_swap import MSDOSSwapPartition
from bootstrapvz.common.tools import log_check_call from bootstrapvz.common.tools import log_check_call
@ -28,7 +29,7 @@ class MSDOSPartitionMap(AbstractPartitionMap):
if 'boot' in data: if 'boot' in data:
self.boot = MSDOSPartition(Sectors(data['boot']['size'], sector_size), self.boot = MSDOSPartition(Sectors(data['boot']['size'], sector_size),
data['boot']['filesystem'], data['boot'].get('format_command', None), data['boot']['filesystem'], data['boot'].get('format_command', None),
last_partition()) data['boot'].get('mountopts', None), 'boot', last_partition())
self.partitions.append(self.boot) self.partitions.append(self.boot)
# Offset all partitions by 1 sector. # Offset all partitions by 1 sector.
@ -46,12 +47,19 @@ class MSDOSPartitionMap(AbstractPartitionMap):
self.root = MSDOSPartition(Sectors(data['root']['size'], sector_size), self.root = MSDOSPartition(Sectors(data['root']['size'], sector_size),
data['root']['filesystem'], data['root'].get('format_command', None), data['root']['filesystem'], data['root'].get('format_command', None),
last_partition()) data['root'].get('mountopts', None), 'root', last_partition())
if self.root.previous is not None: if self.root.previous is not None:
self.root.pad_start += partition_gap self.root.pad_start += partition_gap
self.root.size -= partition_gap self.root.size -= partition_gap
self.partitions.append(self.root) self.partitions.append(self.root)
# Raise exception while trying to create additional partitions
# as its hard to calculate the actual size of the extended partition ATM
# And anyhow - we should go with GPT...
for partition in data:
if partition not in ["boot", "swap", "root", "type"]:
raise PartitionError("If you want to have additional partitions please use GPT partition scheme")
# Mark boot as the boot partition, or root, if boot does not exist # Mark boot as the boot partition, or root, if boot does not exist
getattr(self, 'boot', self.root).flags.append('boot') getattr(self, 'boot', self.root).flags.append('boot')

View file

@ -17,7 +17,7 @@ class NoPartitions(object):
# In the NoPartitions partitions map we only have a single 'partition' # In the NoPartitions partitions map we only have a single 'partition'
self.root = SinglePartition(Sectors(data['root']['size'], sector_size), self.root = SinglePartition(Sectors(data['root']['size'], sector_size),
data['root']['filesystem'], data['root'].get('format_command', None)) data['root']['filesystem'], data['root'].get('format_command', None), data['root'].get('mount_opts', None))
self.partitions = [self.root] self.partitions = [self.root]
def is_blocking(self): def is_blocking(self):

View file

@ -19,7 +19,7 @@ class AbstractPartition(FSMProxy):
{'name': 'unmount', 'src': 'mounted', 'dst': 'formatted'}, {'name': 'unmount', 'src': 'mounted', 'dst': 'formatted'},
] ]
def __init__(self, size, filesystem, format_command): def __init__(self, size, filesystem, format_command, mountopts):
""" """
:param Bytes size: Size of the partition :param Bytes size: Size of the partition
:param str filesystem: Filesystem the partition should be formatted with :param str filesystem: Filesystem the partition should be formatted with
@ -28,6 +28,8 @@ class AbstractPartition(FSMProxy):
self.size = size self.size = size
self.filesystem = filesystem self.filesystem = filesystem
self.format_command = format_command self.format_command = format_command
# List of mount options
self.mountopts = mountopts
# Initialize the start & end padding to 0 sectors, may be changed later # Initialize the start & end padding to 0 sectors, may be changed later
self.pad_start = Sectors(0, size.sector_size) self.pad_start = Sectors(0, size.sector_size)
self.pad_end = Sectors(0, size.sector_size) self.pad_end = Sectors(0, size.sector_size)
@ -80,7 +82,12 @@ class AbstractPartition(FSMProxy):
def _before_mount(self, e): def _before_mount(self, e):
"""Mount the partition """Mount the partition
""" """
log_check_call(['mount', '--types', self.filesystem, self.device_path, e.destination]) if self.mountopts is None:
mount_command = ['mount', '--types', self.filesystem, self.device_path, e.destination]
else:
mount_command = ['mount', '--options', ",".join(self.mountopts), '--types', self.filesystem, self.device_path, e.destination]
# Mount the partition
log_check_call(mount_command)
self.mount_dir = e.destination self.mount_dir = e.destination
def _after_mount(self, e): def _after_mount(self, e):

View file

@ -20,7 +20,7 @@ class BasePartition(AbstractPartition):
{'name': 'unmap', 'src': 'mapped', 'dst': 'unmapped'}, {'name': 'unmap', 'src': 'mapped', 'dst': 'unmapped'},
] ]
def __init__(self, size, filesystem, format_command, previous): def __init__(self, size, filesystem, format_command, mountopts, previous):
""" """
:param Bytes size: Size of the partition :param Bytes size: Size of the partition
:param str filesystem: Filesystem the partition should be formatted with :param str filesystem: Filesystem the partition should be formatted with
@ -34,7 +34,7 @@ class BasePartition(AbstractPartition):
self.flags = [] self.flags = []
# Path to symlink in /dev/disk/by-uuid (manually maintained by this class) # Path to symlink in /dev/disk/by-uuid (manually maintained by this class)
self.disk_by_uuid_path = None self.disk_by_uuid_path = None
super(BasePartition, self).__init__(size, filesystem, format_command) super(BasePartition, self).__init__(size, filesystem, format_command, mountopts)
def create(self, volume): def create(self, volume):
"""Creates the partition """Creates the partition

View file

@ -6,7 +6,7 @@ class GPTPartition(BasePartition):
"""Represents a GPT partition """Represents a GPT partition
""" """
def __init__(self, size, filesystem, format_command, name, previous): def __init__(self, size, filesystem, format_command, mountopts, name, previous):
""" """
:param Bytes size: Size of the partition :param Bytes size: Size of the partition
:param str filesystem: Filesystem the partition should be formatted with :param str filesystem: Filesystem the partition should be formatted with
@ -15,7 +15,7 @@ class GPTPartition(BasePartition):
:param BasePartition previous: The partition that preceeds this one :param BasePartition previous: The partition that preceeds this one
""" """
self.name = name self.name = name
super(GPTPartition, self).__init__(size, filesystem, format_command, previous) super(GPTPartition, self).__init__(size, filesystem, format_command, mountopts, previous)
def _before_create(self, e): def _before_create(self, e):
# Create the partition and then set the name of the partition afterwards # Create the partition and then set the name of the partition afterwards

View file

@ -11,7 +11,7 @@ class GPTSwapPartition(GPTPartition):
:param Bytes size: Size of the partition :param Bytes size: Size of the partition
:param BasePartition previous: The partition that preceeds this one :param BasePartition previous: The partition that preceeds this one
""" """
super(GPTSwapPartition, self).__init__(size, 'swap', None, 'swap', previous) super(GPTSwapPartition, self).__init__(size, 'swap', None, None, 'swap', previous)
def _before_format(self, e): def _before_format(self, e):
log_check_call(['mkswap', self.device_path]) log_check_call(['mkswap', self.device_path])

View file

@ -4,4 +4,13 @@ from base import BasePartition
class MSDOSPartition(BasePartition): class MSDOSPartition(BasePartition):
"""Represents an MS-DOS partition """Represents an MS-DOS partition
""" """
pass def __init__(self, size, filesystem, format_command, mountopts, name, previous):
"""
:param Bytes size: Size of the partition
:param str filesystem: Filesystem the partition should be formatted with
:param list format_command: Optional format command, valid variables are fs, device_path and size
:param str name: The name of the partition
:param BasePartition previous: The partition that preceeds this one
"""
self.name = name
super(MSDOSPartition, self).__init__(size, filesystem, format_command, mountopts, previous)

View file

@ -11,7 +11,7 @@ class MSDOSSwapPartition(MSDOSPartition):
:param Bytes size: Size of the partition :param Bytes size: Size of the partition
:param BasePartition previous: The partition that preceeds this one :param BasePartition previous: The partition that preceeds this one
""" """
super(MSDOSSwapPartition, self).__init__(size, 'swap', None, previous) super(MSDOSSwapPartition, self).__init__(size, 'swap', None, None, 'swap', previous)
def _before_format(self, e): def _before_format(self, e):
log_check_call(['mkswap', self.device_path]) log_check_call(['mkswap', self.device_path])

View file

@ -17,4 +17,4 @@ class UnformattedPartition(BasePartition):
:param Bytes size: Size of the partition :param Bytes size: Size of the partition
:param BasePartition previous: The partition that preceeds this one :param BasePartition previous: The partition that preceeds this one
""" """
super(UnformattedPartition, self).__init__(size, None, None, previous) super(UnformattedPartition, self).__init__(size, None, None, None, previous)

View file

@ -153,7 +153,7 @@ definitions:
properties: properties:
root: {$ref: '#/definitions/partition'} root: {$ref: '#/definitions/partition'}
type: {enum: [none]} type: {enum: [none]}
required: [root] required: [type, root]
additionalProperties: false additionalProperties: false
partition: partition:
type: object type: object
@ -164,21 +164,25 @@ definitions:
items: {type: string} items: {type: string}
minItems: 1 minItems: 1
type: array type: array
mountopts:
items: {type: string}
minItems: 1
type: array
size: {$ref: '#/definitions/bytes'} size: {$ref: '#/definitions/bytes'}
required: [size, filesystem] required: [size, filesystem]
additionalProperties: false additionalProperties: false
partition_table: partition_table:
type: object type: object
patternProperties:
^(?!type|swap).*: {$ref: '#/definitions/partition'}
properties: properties:
boot: {$ref: '#/definitions/partition'}
root: {$ref: '#/definitions/partition'}
swap: swap:
type: object type: object
properties: properties:
size: {$ref: '#/definitions/bytes'} size: {$ref: '#/definitions/bytes'}
required: [size] required: [size]
type: {enum: [msdos, gpt]} type: {enum: [msdos, gpt]}
required: [root] required: [type, root]
additionalProperties: false additionalProperties: false
path: path:
type: string type: string

View file

@ -74,6 +74,7 @@ boot_partition_group = [filesystem.CreateBootMountDir,
mounting_group = [filesystem.CreateMountDir, mounting_group = [filesystem.CreateMountDir,
filesystem.MountRoot, filesystem.MountRoot,
filesystem.MountAdditional,
filesystem.MountSpecials, filesystem.MountSpecials,
filesystem.CopyMountTable, filesystem.CopyMountTable,
filesystem.RemoveMountTable, filesystem.RemoveMountTable,

View file

@ -99,6 +99,33 @@ class MountBoot(Task):
p_map.root.add_mount(p_map.boot, 'boot') p_map.root.add_mount(p_map.boot, 'boot')
class MountAdditional(Task):
description = 'Mounting additional partitions'
phase = phases.volume_mounting
predecessors = [MountRoot]
@classmethod
def run(cls, info):
import os
from bootstrapvz.base.fs.partitions.unformatted import UnformattedPartition
def is_additional(partition):
return (not isinstance(partition, UnformattedPartition) and
partition.name not in ["boot", "swap", "root"])
p_map = info.volume.partition_map
partitions = p_map.partitions
for partition in sorted(
filter(is_additional, partitions),
key=lambda partition: len(partition.name)):
partition = getattr(p_map, partition.name)
os.makedirs(os.path.join(info.root, partition.name))
if partition.mountopts is None:
p_map.root.add_mount(getattr(p_map, partition.name), partition.name)
else:
p_map.root.add_mount(getattr(p_map, partition.name), partition.name, ['--options'] + partition.mountopts)
class MountSpecials(Task): class MountSpecials(Task):
description = 'Mounting special block devices' description = 'Mounting special block devices'
phase = phases.os_installation phase = phases.os_installation
@ -165,7 +192,14 @@ class FStab(Task):
@classmethod @classmethod
def run(cls, info): def run(cls, info):
import os.path import os.path
from bootstrapvz.base.fs.partitions.unformatted import UnformattedPartition
def is_additional(partition):
return (not isinstance(partition, UnformattedPartition) and
partition.name not in ["boot", "swap", "root"])
p_map = info.volume.partition_map p_map = info.volume.partition_map
partitions = p_map.partitions
mount_points = [{'path': '/', mount_points = [{'path': '/',
'partition': p_map.root, 'partition': p_map.root,
'dump': '1', 'dump': '1',
@ -184,10 +218,21 @@ class FStab(Task):
'pass_num': '0', 'pass_num': '0',
}) })
for partition in sorted(
filter(is_additional, partitions),
key=lambda partition: len(partition.name)):
mount_points.append({'path': "/" + partition.name,
'partition': getattr(p_map, partition.name),
'dump': '1',
'pass_num': '2',
})
fstab_lines = [] fstab_lines = []
for mount_point in mount_points: for mount_point in mount_points:
partition = mount_point['partition'] partition = mount_point['partition']
mount_opts = ['defaults'] if partition.mountopts is None:
mount_opts = ['defaults']
else:
mount_opts = partition.mountopts
fstab_lines.append('UUID={uuid} {mountpoint} {filesystem} {mount_opts} {dump} {pass_num}' fstab_lines.append('UUID={uuid} {mountpoint} {filesystem} {mount_opts} {dump} {pass_num}'
.format(uuid=partition.get_uuid(), .format(uuid=partition.get_uuid(),
mountpoint=mount_point['path'], mountpoint=mount_point['path'],

View file

@ -302,17 +302,29 @@ boot, root and swap.
- ``{fs}``: The filesystem of the partition. - ``{fs}``: The filesystem of the partition.
- ``{device_path}``: The device path of the partition. - ``{device_path}``: The device path of the partition.
- ``{size}``: The size of the partition. - ``{size}``: The size of the partition.
- ``{mount_opts}``: Options to mount the partition with. This optional
setting overwrites the default option list bootstrap-vz would
normally use to mount the partiton (defaults). The List is specified
as a string array where each option/argument is an item in that array.
``optional`` Here some examples:
- ``nodev``
- ``nosuid``
- ``noexec``
- ``journal_ioprio=3``
The default command used by boostrap-vz is The default command used by boostrap-vz is
``['mkfs.{fs}', '{device_path}']``. ``['mkfs.{fs}', '{device_path}']``.
- ``boot``: Configuration of the boot partition. The three - ``boot``: Configuration of the boot partition. All settings equal
settings equal those of the root partition. those of the root partition.
``optional`` ``optional``
- ``swap``: Configuration of the swap partition. Since the swap - ``swap``: Configuration of the swap partition. Since the swap
partition has its own filesystem you can only specify the size for partition has its own filesystem you can only specify the size for
this partition. this partition.
``optional`` ``optional``
- ``additional_path``: Configuration of additional partitions. (e.g. /var/tmp)
All settings equal those of the root partition.
Example: Example:

View file

@ -0,0 +1,46 @@
---
name: debian-{system.release}-{system.architecture}-{%Y}{%m}{%d}
provider:
name: kvm
virtio_modules:
- virtio_pci
- virtio_blk
bootstrapper:
workspace: /target
system:
release: stretch
architecture: amd64
bootloader: grub
charmap: UTF-8
locale: en_US
timezone: UTC
volume:
backing: raw
partitions:
type: gpt
boot:
filesystem: ext2
size: 1GiB
swap:
size: 128MiB
root:
filesystem: ext4
size: 8GiB
tmp:
mountopts:
- nodev
- noexec
- nosuid
- journal_ioprio=3
filesystem: ext4
size: 1GiB
var:
filesystem: ext4
size: 1GiB
var/tmp:
filesystem: ext4
size: 1GiB
plugins:
root_password:
password: test