summaryrefslogtreecommitdiffstats
path: root/roles
diff options
context:
space:
mode:
authorAhmed Abdelhalim <[email protected]>2025-08-11 00:33:29 +0200
committerAhmed Abdelhalim <[email protected]>2025-08-11 00:33:29 +0200
commitbc12fd286b5916518ea5ca756b9ea6eac99c63ef (patch)
tree5d968c9e192201e416e9af9e40aaec9338069123 /roles
parent015c3758d922cd2e06290c460bc34815088848c2 (diff)
Add partition role
Diffstat (limited to 'roles')
-rw-r--r--roles/partition/README.md38
-rw-r--r--roles/partition/defaults/main.yml3
-rw-r--r--roles/partition/meta/argument_specs.yml120
-rw-r--r--roles/partition/meta/main.yml19
-rw-r--r--roles/partition/tasks/main.yml99
5 files changed, 279 insertions, 0 deletions
diff --git a/roles/partition/README.md b/roles/partition/README.md
new file mode 100644
index 0000000..e9fae12
--- /dev/null
+++ b/roles/partition/README.md
@@ -0,0 +1,38 @@
+# Ansible Role: partition
+
+This role partitions the desired host with defined partition map
+
+## Role Variables
+
+- `partition_disk` the `dev/disk` to be partitioned (required)
+- `partition_wipe` whether or not to wipe the disk before partitioning (default: false)
+- `partition_root` partition map (defined below)
+- `partition_boot` partition map (defined below)
+- `partition_swap` partition map (defined below)
+- `partition_extras` list of partition map (defined below) default: []
+
+### Partition map
+
+This is the `dict` defining the partition properties:
+
+- `num` partition number (required)
+- `name` partition name (required)
+- `label` partition label (default `gpt`) check `argument_specs` for available labels
+- `flags` partition flags (default `[]`) check `argument_specs` for available flags
+- `part_start` address to start partition (required, ex: `11G`, `0%`)
+- `part_end` address to end partition (required, ex: `250G`, `100%`)
+- `fstype` partition file system type (required) check `argument_specs` for available file systems
+- `fstype_opts` list of options to be passed to `mkfs` (default: `""`)
+- `dev` device mounted path (required, ex: `/dev/nvme0n1p4`, `/dev/sdb4`)
+- `mount_path` mount path directory of the partition (required, ex: `/mnt`)
+- `fstab_opts` options to add to `fstab` mount (default `"defaults"`)
+
+## Internals
+
+- force erase/wipe partition when configured with `partition_wipe: true`
+- ensure swap is unmounted (to avoid failures on multiple consequent runs)
+- ensure partitions are unmounted (respecting mount tree), ex. `/mnt/dir/sub` is unmounted before `/mnt/dir`
+- partition the partition list provided
+- create (force) file system on partitions
+- create swap partition (using `mkswap` and `swapon` as ansible parted doesn't support that)
+- mount partitions (respecting mount tree), ex. `/mnt` is mounted before `/mnt/dir` before `/mnt/dir/sub`
diff --git a/roles/partition/defaults/main.yml b/roles/partition/defaults/main.yml
new file mode 100644
index 0000000..3a512fe
--- /dev/null
+++ b/roles/partition/defaults/main.yml
@@ -0,0 +1,3 @@
+---
+partition_wipe: false
+partition_extras: []
diff --git a/roles/partition/meta/argument_specs.yml b/roles/partition/meta/argument_specs.yml
new file mode 100644
index 0000000..9645798
--- /dev/null
+++ b/roles/partition/meta/argument_specs.yml
@@ -0,0 +1,120 @@
+---
+argument_specs:
+ main:
+ options:
+ partition_disk:
+ type: "str"
+ required: true
+ description: "The device to partition. (ex: /dev/nvme0n1)"
+ partition_wipe:
+ type: "bool"
+ default: "false"
+ description: "Force wiping of the partition_disk"
+ partition_boot:
+ type: "dict"
+ description: "The boot partition definition"
+ required: true
+ options: &partition_options
+ num:
+ type: "int"
+ description: "Partition number"
+ required: true
+ name:
+ type: "str"
+ description: "Partition name"
+ required: true
+ label:
+ type: "str"
+ description: "Partition label"
+ default: "gpt"
+ choices:
+ - "aix"
+ - "amiga"
+ - "bsd"
+ - "dvh"
+ - "gpt"
+ - "loop"
+ - "mac"
+ - "msdos"
+ - "pc98"
+ - "sun"
+ flags:
+ type: "list"
+ description: "Partition flags"
+ default: []
+ elements: "str"
+ choices:
+ - "atvrecv"
+ - "bios_grup"
+ - "boot"
+ - "DIAG"
+ - "esp"
+ - "hidden"
+ - "hp-service"
+ - "irst"
+ - "lba"
+ - "legacy_boot"
+ - "LVM"
+ - "msftdata"
+ - "msftres"
+ - "PALO"
+ - "PREP"
+ - "raid"
+ - "root"
+ - "swap"
+ part_start:
+ type: "str"
+ description: "Partition start (ex: 10GiB, 15%)"
+ required: true
+ part_end:
+ type: "str"
+ description: "Partition end (ex: 11GiB, 100%)"
+ required: true
+ fstype:
+ type: "str"
+ description: "Partition filesystem type"
+ required: true
+ choices:
+ - "bcachefs"
+ - "btrfs"
+ - "ext2"
+ - "ext3"
+ - "ext4"
+ - "ext4dev"
+ - "f2fs"
+ - "lvm"
+ - "ocfs2"
+ - "reiserfs"
+ - "xfs"
+ - "vfat"
+ - "swap"
+ - "ufs"
+ fstype_opts:
+ type: "str"
+ description: "List of options to be passed to mkfs command"
+ default: ""
+ dev:
+ type: "str"
+ description: "Partition device path (ex: /dev/nvme0n1p3) ignore for partitions not used in playbook"
+ mount_path:
+ type: "str"
+ description: "Partition mount path (ex: /mnt/home), ignore for partitions not used in playbook"
+ fstab_opts:
+ type: "str"
+ description: "Options used for mounting (fstab options)"
+ default: "defaults"
+ partition_swap:
+ type: "dict"
+ description: "The swap partition definition"
+ options: *partition_options
+ partition_root:
+ description: "The root partition definition"
+ required: true
+ options: *partition_options
+ partition_extras:
+ type: "list"
+ description: "The list of extra partitions"
+ required: false
+ default: []
+ elements: "dict"
+ options: *partition_options
diff --git a/roles/partition/meta/main.yml b/roles/partition/meta/main.yml
new file mode 100644
index 0000000..3872e6c
--- /dev/null
+++ b/roles/partition/meta/main.yml
@@ -0,0 +1,19 @@
+---
+dependencies: []
+galaxy_info:
+ role_name: partition
+ author: a14m
+ description: "Create and manage device partitions"
+ company: "kartoffeln.work GmbH."
+ license: "license MIT"
+ min_ansible_version: "2.18"
+ platforms:
+ - name: ArchLinux
+ versions:
+ - all
+ - name: Ubuntu
+ versions:
+ - noble
+ - name: Debian
+ versions:
+ - bookworm
diff --git a/roles/partition/tasks/main.yml b/roles/partition/tasks/main.yml
new file mode 100644
index 0000000..faa6bd0
--- /dev/null
+++ b/roles/partition/tasks/main.yml
@@ -0,0 +1,99 @@
+---
+- name: "Ensure partition_swap is defined"
+ ansible.builtin.set_fact:
+ partition_swap: "{{ partition_swap | default({'name': 'default-swap'}) }}"
+
+- name: "Set partition_list"
+ ansible.builtin.set_fact:
+ partition_list: >-
+ {{
+ ([partition_boot, partition_swap, partition_root] + partition_extras)
+ | rejectattr('num', 'undefined')
+ | sort(attribute="num")
+ }}
+
+- name: "Show partition_list"
+ ansible.builtin.debug:
+ msg: "{{ partition_list }}"
+
+- name: "Erase device: {{ partition_disk }}"
+ ansible.builtin.command:
+ cmd: "wipefs -af {{ partition_disk }}"
+ when: partition_wipe
+ changed_when: true
+
+- name: "Run swapoff {{ partition_swap.dev }}"
+ # Ensure unmounting swap partition as it's mounted using swapon
+ ansible.builtin.command:
+ cmd: "swapoff {{ partition_swap.dev }}"
+ changed_when: true
+ failed_when: false
+ when: partition_swap.dev is defined
+
+- name: "Ensure partitions are not mounted"
+ ansible.posix.mount:
+ path: "{{ item.mount_path | default('') }}"
+ state: "unmounted"
+ # Ensure the /mnt/path is unmounted before /mnt (respect mount tree)
+ # This ensures not getting error device is busy when unmounting
+ # Ignore partitions without mount_path
+ with_items: "{{
+ partition_list
+ | rejectattr('mount_path', 'undefined')
+ | sort(attribute='mount_path')
+ | reverse
+ }}"
+
+- name: "Partition device: {{ partition_disk }}"
+ community.general.parted:
+ device: "{{ partition_disk }}"
+ number: "{{ item.num }}"
+ name: "{{ item.name }}"
+ label: "{{ item.label | default('gpt') }}"
+ flags: "{{ item.flags | default([]) }}"
+ part_start: "{{ item.part_start }}"
+ part_end: "{{ item.part_end }}"
+ state: "present"
+ with_items: "{{ partition_list }}"
+
+- name: "Create filesystem on device: {{ partition_disk }}"
+ community.general.filesystem:
+ dev: "{{ item.dev }}"
+ fstype: "{{ item.fstype }}"
+ opts: "{{ item.fstype_opts | default(omit) }}"
+ # For swap partiton and root partitions, always force recreation of the filesystem
+ # For any other partition, only create the filesystem if partition_wipe is true
+ # This is done to ensure that new install a distro won't affect other already installed systems.
+ force: "{{ (item.name in [partition_swap.name, partition_root.name]) | ternary(true, partition_wipe) }}"
+ with_items: "{{ partition_list }}"
+
+- name: "Run mkswap"
+ ansible.builtin.command:
+ cmd: "mkswap {{ partition_swap.dev }}"
+ changed_when: true
+ when: partition_swap.dev is defined
+
+- name: "Run swapon {{ partition_swap.dev }}"
+ ansible.builtin.command:
+ cmd: "swapon {{ partition_swap.dev }}"
+ changed_when: true
+ when: partition_swap.dev is defined
+
+- name: "Mount partitions"
+ ansible.posix.mount:
+ src: "{{ item.dev }}"
+ path: "{{ item.mount_path }}"
+ fstype: "{{ item.fstype }}"
+ opts: "{{ item.fstab_opts | default('defaults') }}"
+ state: "mounted"
+ # Ignore mounting partitions without mount_path
+ when: not (item.mount_path is undefined)
+ # Ignore mounting swap partition as it's mounted using swapon
+ # Ignore partitions without mount_path
+ # Ensure the /mnt is mounted before /mnt/path (respect mount tree)
+ with_items: "{{
+ partition_list
+ | difference([partition_swap])
+ | rejectattr('mount_path', 'undefined')
+ | sort(attribute='mount_path')
+ }}"