diff --git a/CHANGELOG.md b/CHANGELOG.md
index a4361a998..0da437282 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
+* **role:alternatives**: Support managing `subcommands` (slaves/followers) and the Red Hat-only `family` grouping. The role now also ensures the alternatives tooling is installed (`chkconfig` on RHEL 8, `alternatives` on RHEL 9/10; bundled with `dpkg` on Debian/Ubuntu), and can be included without variables as a no-op.
* **role:mariadb_server**: Add `mariadb_server__cnf_innodb_snapshot_isolation` variable (MariaDB 10.6+), defaulting to `'ON'`.
### Security
diff --git a/roles/alternatives/README.md b/roles/alternatives/README.md
index 2a7e132b3..f4015defd 100644
--- a/roles/alternatives/README.md
+++ b/roles/alternatives/README.md
@@ -12,31 +12,43 @@ Hints:
*Available since LFOps `3.0.0`.*
+## How the Role Behaves
+
+* The role ensures the alternatives tooling is installed: the `chkconfig` package on RHEL 8, the `alternatives` package on RHEL 9 and 10. On Debian and Ubuntu, `update-alternatives` ships with `dpkg`, so nothing is installed there.
+* On Red Hat-family hosts `link` is mandatory for each entry. On Debian-based hosts it is only required when the alternative `name` is unknown to the system.
+* `family` is only available on Red Hat-family hosts (community.general 10.1.0+); `subcommands` requires community.general 5.1.0+. Both are passed through only when set, so they do not affect entries or platforms that do not use them.
+
+
## Tags
`alternatives`
-* Manages alternative programs for common commands.
+* Installs the alternatives tooling and manages alternative programs for common commands.
* Triggers: none.
-## Mandatory Role Variables
+## Optional Role Variables
`alternatives__alternatives`
-* List of alternatives to remove or to deploy.
+* List of alternatives to remove or to deploy. With the default empty list, the role is a no-op.
* Type: List of dictionaries.
* Default: `[]`
* Subkeys:
- * `link`:
+ * `name`:
- * The path to the symbolic link that should point to the real executable. This option is always required on RHEL-based distributions. On Debian-based distributions this option is required when the alternative `name` is unknown to the system.
+ * Mandatory. The generic name of the link.
* Type: String.
- * `name`:
+ * `family`:
- * Mandatory. The generic name of the link.
+ * Optional. Groups similar alternatives. Red Hat-family only.
+ * Type: String.
+
+ * `link`:
+
+ * The path to the symbolic link that should point to the real executable. This option is always required on RHEL-based distributions. On Debian-based distributions this option is required when the alternative `name` is unknown to the system.
* Type: String.
* `path`:
@@ -46,7 +58,7 @@ Hints:
* `priority`:
- * Optional. The priority of the alternative. If no priority is given for creation `50` is used as a fallback.
+ * Optional. The priority of the alternative. If no priority is given, the `alternatives` tool uses `50` as a fallback on creation.
* Type: Number.
* `state`:
@@ -55,20 +67,31 @@ Hints:
* Type: String.
* Default: `'selected'`
+ * `subcommands`:
+
+ * Optional. Subcommands (also called slaves or followers) that are switched together with this alternative. Each entry needs `name`, `link`, and `path`.
+ * Type: List of dictionaries.
+
Example:
```yaml
alternatives__alternatives:
- - link: '/usr/bin/python3'
- name: 'python3'
- path: '/usr/bin/python3.9'
- priority: '100' # install python3.9 with higher priority
- state: 'auto'
- - link: '/usr/bin/python3'
- name: 'python3'
+ # set the default python3, also switching its subcommands (slaves)
+ - name: 'python3'
+ link: '/usr/bin/python3'
path: '/usr/bin/python3.12'
- priority: '10'
+ priority: 100
state: 'auto'
-# => results in python3.9
+ subcommands:
+ - name: 'python3-config'
+ link: '/usr/bin/python3-config'
+ path: '/usr/bin/python3.12-config'
+ # Red Hat-family only: select an alternative by family
+ - name: 'java'
+ family: 'java-11-openjdk.x86_64'
+ # remove an alternative
+ - name: 'editor'
+ path: '/usr/bin/vim'
+ state: 'absent'
```
diff --git a/roles/alternatives/defaults/main.yml b/roles/alternatives/defaults/main.yml
new file mode 100644
index 000000000..199ab387f
--- /dev/null
+++ b/roles/alternatives/defaults/main.yml
@@ -0,0 +1 @@
+alternatives__alternatives: []
diff --git a/roles/alternatives/meta/argument_specs.yml b/roles/alternatives/meta/argument_specs.yml
index 4a2d84806..8700dd97b 100644
--- a/roles/alternatives/meta/argument_specs.yml
+++ b/roles/alternatives/meta/argument_specs.yml
@@ -5,11 +5,19 @@ argument_specs:
alternatives__alternatives:
type: 'list'
elements: 'dict'
- required: true
+ required: false
+ default: []
description: >-
List of `update-alternatives` entries to manage.
options:
+ family:
+ type: 'str'
+ required: false
+ description: >-
+ Groups similar alternatives. Red Hat-family only
+ (community.general 10.1.0+).
+
link:
type: 'str'
required: false
@@ -33,8 +41,8 @@ argument_specs:
type: 'int'
required: false
description: >-
- Priority weight for `auto` mode (default fallback for
- creation: 50).
+ Priority weight for `auto` mode. If unset, the module
+ falls back to 50 on creation.
state:
type: 'str'
@@ -47,3 +55,27 @@ argument_specs:
- 'selected'
description: >-
`selected` (default), `present`, `auto`, or `absent`.
+
+ subcommands:
+ type: 'list'
+ elements: 'dict'
+ required: false
+ description: >-
+ Subcommands (also called slaves or followers) switched
+ together with this alternative (community.general 5.1.0+).
+ options:
+
+ link:
+ type: 'str'
+ required: true
+ description: 'Path to the subcommand symlink.'
+
+ name:
+ type: 'str'
+ required: true
+ description: 'The generic name of the subcommand.'
+
+ path:
+ type: 'str'
+ required: true
+ description: 'Path to the real subcommand executable.'
diff --git a/roles/alternatives/tasks/main.yml b/roles/alternatives/tasks/main.yml
index 09331ace7..e5cd6c786 100644
--- a/roles/alternatives/tasks/main.yml
+++ b/roles/alternatives/tasks/main.yml
@@ -1,5 +1,25 @@
- block:
+ - name: 'Set platform/version specific variables'
+ ansible.builtin.import_role:
+ name: 'shared'
+ tasks_from: 'platform-variables.yml'
+
+ tags:
+ - 'always'
+
+
+- block:
+
+ # Debian/Ubuntu ship update-alternatives with dpkg, so __alternatives__packages
+ # is empty there and this task is skipped.
+ - name: 'Install the alternatives tooling'
+ ansible.builtin.package:
+ name: '{{ __alternatives__packages }}'
+ state: 'present'
+ when:
+ - '__alternatives__packages | length > 0'
+
- name: 'Debug'
ansible.builtin.debug:
msg:
@@ -9,11 +29,13 @@
# alternatives --install
- name: 'Manages alternative programs for common commands'
community.general.alternatives:
- link: '{{ item["link"] }}'
name: '{{ item["name"] }}'
- path: '{{ item["path"] }}'
- priority: '{{ item["priority"] | d(50) }}'
+ link: '{{ item["link"] | default(omit) }}'
+ path: '{{ item["path"] | default(omit) }}'
+ priority: '{{ item["priority"] | default(omit) }}'
state: '{{ item["state"] | d("selected") }}'
+ subcommands: '{{ item["subcommands"] | default(omit) }}'
+ family: '{{ item["family"] | default(omit) }}'
loop: '{{ alternatives__alternatives }}'
tags:
diff --git a/roles/alternatives/vars/RedHat10.yml b/roles/alternatives/vars/RedHat10.yml
new file mode 100644
index 000000000..8a9bd27d0
--- /dev/null
+++ b/roles/alternatives/vars/RedHat10.yml
@@ -0,0 +1,3 @@
+# Since RHEL 9 the `alternatives` command has its own package.
+__alternatives__packages:
+ - 'alternatives'
diff --git a/roles/alternatives/vars/RedHat8.yml b/roles/alternatives/vars/RedHat8.yml
new file mode 100644
index 000000000..02373be25
--- /dev/null
+++ b/roles/alternatives/vars/RedHat8.yml
@@ -0,0 +1,3 @@
+# On RHEL 8 the `alternatives` command is provided by the chkconfig package.
+__alternatives__packages:
+ - 'chkconfig'
diff --git a/roles/alternatives/vars/RedHat9.yml b/roles/alternatives/vars/RedHat9.yml
new file mode 100644
index 000000000..8a9bd27d0
--- /dev/null
+++ b/roles/alternatives/vars/RedHat9.yml
@@ -0,0 +1,3 @@
+# Since RHEL 9 the `alternatives` command has its own package.
+__alternatives__packages:
+ - 'alternatives'
diff --git a/roles/alternatives/vars/main.yml b/roles/alternatives/vars/main.yml
new file mode 100644
index 000000000..b36c84c80
--- /dev/null
+++ b/roles/alternatives/vars/main.yml
@@ -0,0 +1,3 @@
+# Base default. The alternatives tooling ships with dpkg on Debian/Ubuntu, so
+# nothing needs to be installed there. Red Hat-family overrides this per version.
+__alternatives__packages: []