Skip to content

Commit

Permalink
feat: add support for node attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
tomjelinek committed Feb 5, 2024
1 parent 2450454 commit 203212e
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 12 deletions.
2 changes: 2 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ variables:

.tier1_tests: &TIER1_TESTS
- cib_constraints_create
- cib_node_attributes
- cib_properties_empty
- cib_properties_one_set
- cib_resources_create
Expand All @@ -49,6 +50,7 @@ variables:
- cluster_basic
- cluster_destroy
- default
- node_options_check
- qdevice_all_options
- qdevice_minimal
- qdevice_tls_kaptb_options
Expand Down
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ An Ansible role for managing High Availability Clustering.
* resource defaults and resource operation defaults
* stonith levels, also known as fencing topology
* resource constraints
* Pacemaker node attributes

## Requirements

Expand Down Expand Up @@ -489,6 +490,41 @@ Currently, only one set is supported.

You may take a look at [an example](#configuring-cluster-properties).

#### `ha_cluster_node_options`

structure, default: no node options

```yaml
ha_cluster_node_options:
- node_name: node1
attributes:
- attrs:
- name: attribute1
value: value1_node1
- name: attribute2
value: value2_node1
- node_name: node2
attributes:
- attrs:
- name: attribute1
value: value1_node2
- name: attribute2
value: value2_node2
```

This variable defines various settings which vary from cluster node to cluster
node. **Note:** Use an inventory or playbook hosts to specify which nodes form
the cluster. This variable merely sets options for the specified nodes. The
items are as follows:

* `node_name` (mandatory) - Node name.
* `attributes` (optional) - List of sets of Pacemaker node attributes for the
node. Currently, no more than one set for each node is supported.

You may take a look at examples:

* [configuring node attributes](#configuring-node-attributes)

#### `ha_cluster_resource_primitives`

structure, default: no resources
Expand Down Expand Up @@ -1855,6 +1891,33 @@ Note that you cannot run a quorum device on a cluster node.
- linux-system-roles.ha_cluster
```
### Configuring node attributes
```yaml
- hosts: node1 node2
vars:
ha_cluster_cluster_name: my-new-cluster
ha_cluster_hacluster_password: password
ha_cluster_node_options:
- node_name: node1
attributes:
- attrs:
- name: attribute1
value: value1A
- name: attribute2
value: value2A
- node_name: node2
attributes:
- attrs:
- name: attribute1
value: value1B
- name: attribute2
value: value2B

roles:
- linux-system-roles.ha_cluster
```
### Purging all cluster configuration
```yaml
Expand Down
1 change: 1 addition & 0 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ ha_cluster_pcs_permission_list:
- write

ha_cluster_cluster_properties: []
ha_cluster_node_options: []
ha_cluster_resource_defaults: {}
ha_cluster_resource_operation_defaults: {}

Expand Down
27 changes: 27 additions & 0 deletions examples/node-attributes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# SPDX-License-Identifier: MIT
---
- name: Example ha_cluster role invocation - node attributes definition
hosts: all
vars:
ha_cluster_manage_firewall: true
ha_cluster_manage_selinux: true
ha_cluster_cluster_name: my-new-cluster
ha_cluster_hacluster_password: password
ha_cluster_node_options:
- node_name: node1
attributes:
- attrs:
- name: attribute1
value: value1A
- name: attribute2
value: value2A
- node_name: node2
attributes:
- attrs:
- name: attribute1
value: value1B
- name: attribute2
value: value2B

roles:
- linux-system-roles.ha_cluster
43 changes: 32 additions & 11 deletions tasks/shell_pcs/check-and-prepare-role-variables.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# SPDX-License-Identifier: MIT
---
- name: Discover cluster node names
set_fact:
__ha_cluster_node_name: "{{ ha_cluster.node_name | d(inventory_hostname) }}"

- name: Collect cluster node names
set_fact:
__ha_cluster_all_node_names: "{{
ansible_play_hosts
| map('extract', hostvars, '__ha_cluster_node_name')
| list
}}"

- name: Check cluster configuration variables
block:
- name: Fail if passwords are not specified
Expand Down Expand Up @@ -59,17 +71,26 @@
loop: "{{ ha_cluster_stonith_levels }}"
run_once: true

- name: Discover cluster node names
set_fact:
__ha_cluster_node_name: "{{ ha_cluster.node_name | d(inventory_hostname) }}"

- name: Collect cluster node names
set_fact:
__ha_cluster_all_node_names: "{{
ansible_play_hosts
| map('extract', hostvars, '__ha_cluster_node_name')
| list
}}"
- name: Check ha_cluster_node_options
run_once: true
vars:
__nodes_from_options: "{{
ha_cluster_node_options | map(attribute='node_name') | list }}"
block:
- name: >
Fail if ha_cluster_node_options contains unknown or duplicate nodes
fail:
msg: >
node_name fields in ha_cluster_node_options must be unique
and they must match cluster nodes
when:
- >
(
(__nodes_from_options | length) !=
(__nodes_from_options | unique | length)
) or (
__nodes_from_options | difference(__ha_cluster_all_node_names)
)
- name: Extract qdevice settings
set_fact:
Expand Down
11 changes: 10 additions & 1 deletion tasks/shell_pcs/create-and-push-cib.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,22 @@
## Cluster properties
- name: Configure cluster properties
include_tasks: pcs-cluster-properties.yml
# pcs-0.10 supports only one set of attributes, that's the reason for
# pcs-0.11 supports only one set of attributes, that's the reason for
# 'ha_cluster_cluster_properties[0]' and 'when' instead of looping over
# ha_cluster_cluster_properties
vars:
properties_set: "{{ ha_cluster_cluster_properties[0] }}"
when: ha_cluster_cluster_properties[0].attrs | d([])

## Node attributes
- name: Configure node attributes
include_tasks: pcs-node-attributes.yml
loop: "{{ ha_cluster_node_options | default([]) |
selectattr('attributes', 'defined') | list }}"
loop_control:
loop_var: node_options

## Resource and operation defaults
# RHEL 8.3, which is the oldest version supported by the role, supports
# multiple sets of resources and resources operations defaults. Therefore,
# we don't need any checks for pcs capabilities.
Expand Down
20 changes: 20 additions & 0 deletions tasks/shell_pcs/pcs-node-attributes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: MIT
---
- name: Configure node attributes set for node {{ node_options.node_name }}
command:
# Multiple sets of attributes (with rules) per node are not supported by
# pcs (and therefore the role) as of yet
cmd: >
pcs -f {{ __ha_cluster_tempfile_cib_xml.path | quote }}
-- node attribute {{ node_options.node_name | quote }}
{% for attr_set in node_options.attributes | d([], true) %}
{% for attr in attr_set.attrs | d([]) %}
{{ attr.name | quote }}={{ attr.value | quote }}
{% endfor %}
{% endfor %}
# We always need to create CIB to see whether it's the same as what is
# already present in the cluster. However, we don't want to report it as a
# change since the only thing which matters is pushing the resulting CIB to
# the cluster.
check_mode: false
changed_when: not ansible_check_mode
24 changes: 24 additions & 0 deletions tests/tasks/assert_node_options_check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: MIT
---
- name: Debug ha_cluster_node_options
debug:
var: ha_cluster_node_options

- name: Run the role and check for errors
block:
- name: Run the role
include_role:
name: linux-system-roles.ha_cluster

- name: Fail
fail:
msg: Expected failure did not occur
rescue:
- name: Check errors
assert:
that: ansible_failed_result.msg | trim == expected_msg
run_once: true # noqa: run_once[task]
vars:
expected_msg: >-
node_name fields in ha_cluster_node_options
must be unique and they must match cluster nodes
69 changes: 69 additions & 0 deletions tests/tests_cib_node_attributes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# SPDX-License-Identifier: MIT
---
- name: Configure node attributes
hosts: all
vars_files: vars/main.yml

tasks:
- name: Run test
tags: tests::verify
block:
- name: Set up test environment
include_role:
name: linux-system-roles.ha_cluster
tasks_from: test_setup.yml

- name: Find first node name
set_fact:
__test_first_node: "{{
(ansible_play_hosts_all | length == 1)
| ternary('localhost', ansible_play_hosts[0]) }}"

- name: Run HA Cluster role
include_role:
name: linux-system-roles.ha_cluster
public: true
vars:
ha_cluster_cluster_name: test-cluster
ha_cluster_manage_firewall: true
ha_cluster_manage_selinux: true
ha_cluster_node_options:
- node_name: "{{ __test_first_node }}"
attributes:
- attrs:
- name: attr1
value: val1
- name: attr2
value: val2
- attrs:
- name: attr3
value: val3

- name: Verify node attributes
vars:
__test_expected_lines:
- "Node Attributes:"
- " {{ __test_first_node }}: attr1=val1 attr2=val2 attr3=val3"
block:
- name: Fetch node attributes configuration from the cluster
command:
cmd: pcs node attribute
register: __test_pcs_node_attribute_config
changed_when: false

- name: Print real node attributes configuration
debug:
var: __test_pcs_node_attribute_config

- name: Print expected node attributes configuration
debug:
var: __test_expected_lines | list

- name: Check node attributes configuration
assert:
that:
- __test_pcs_node_attribute_config.stdout_lines
== __test_expected_lines | list

- name: Check firewall and selinux state
include_tasks: tasks/check_firewall_selinux.yml
34 changes: 34 additions & 0 deletions tests/tests_node_options_check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# SPDX-License-Identifier: MIT
---
- name: Verify node_name in ha_cluster_node_options check is working
hosts: all
vars_files: vars/main.yml

tasks:
- name: Run test
tags: tests::verify
block:
- name: Set up test environment
include_role:
name: linux-system-roles.ha_cluster
tasks_from: test_setup.yml

- name: Find first node name
set_fact:
__test_first_node: "{{
(ansible_play_hosts_all | length == 1)
| ternary('localhost', ansible_play_hosts[0]) }}"

- name: Verify node options check is working for various input data
include_tasks: tasks/assert_node_options_check.yml
loop:
# yamllint disable rule:hyphens
-
- node_name: "{{ __test_first_node }}"
- node_name: "{{ __test_first_node }}"
-
- node_name: "{{ __test_first_node }}"
- node_name: "{{ __test_first_node ~ 'x' }}"
# yamllint enable rule:hyphens
loop_control:
loop_var: ha_cluster_node_options

0 comments on commit 203212e

Please sign in to comment.