Skip to content

Commit 3baa13a

Browse files
munchtoastfelixfonteinrussoz
authored
pacemaker_resource: Add cloning support for resources and groups (#10665)
* add clone state for pacemaker_resource * add changelog fragment * Additional description entry for comment header * Apply suggestions from code review Co-authored-by: Felix Fontein <[email protected]> * Update plugins/modules/pacemaker_resource.py Co-authored-by: Felix Fontein <[email protected]> * fix formatting for yamllint * Apply code review suggestions * refactor state name to cloned * Update plugins/modules/pacemaker_resource.py Co-authored-by: Felix Fontein <[email protected]> * Apply suggestions from code review Co-authored-by: Alexei Znamensky <[email protected]> * Apply suggestions from code review --------- Co-authored-by: Felix Fontein <[email protected]> Co-authored-by: Alexei Znamensky <[email protected]>
1 parent d0123a1 commit 3baa13a

File tree

4 files changed

+113
-7
lines changed

4 files changed

+113
-7
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
minor_changes:
2+
- pacemaker_resource - add ``state=cloned`` for cloning pacemaker resources or groups (https://github.com/ansible-collections/community.general/issues/10322, https://github.com/ansible-collections/community.general/pull/10665).

plugins/module_utils/pacemaker.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
_state_map = {
1414
"present": "create",
1515
"absent": "remove",
16+
"cloned": "clone",
1617
"status": "status",
1718
"enabled": "enable",
1819
"disabled": "disable",
@@ -65,6 +66,8 @@ def pacemaker_runner(module, **kwargs):
6566
resource_operation=cmd_runner_fmt.as_func(fmt_resource_operation),
6667
resource_meta=cmd_runner_fmt.stack(cmd_runner_fmt.as_opt_val)("meta"),
6768
resource_argument=cmd_runner_fmt.as_func(fmt_resource_argument),
69+
resource_clone_ids=cmd_runner_fmt.as_list(),
70+
resource_clone_meta=cmd_runner_fmt.as_list(),
6871
apply_all=cmd_runner_fmt.as_bool("--all"),
6972
agent_validation=cmd_runner_fmt.as_bool("--agent-validation"),
7073
wait=cmd_runner_fmt.as_opt_eq_val("--wait"),

plugins/modules/pacemaker_resource.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@
2727
state:
2828
description:
2929
- Indicate desired state for cluster resource.
30-
- The state V(cleanup) has been added in community.general 11.3.0.
31-
choices: [present, absent, enabled, disabled, cleanup]
30+
- The states V(cleanup) and V(cloned) have been added in community.general 11.3.0.
31+
- If O(state=cloned) or O(state=present), you can set O(resource_clone_ids) and O(resource_clone_meta) to determine exactly what and how to clone.
32+
choices: [present, absent, cloned, enabled, disabled, cleanup]
3233
default: present
3334
type: str
3435
name:
3536
description:
36-
- Specify the resource name to create.
37+
- Specify the resource name to create or clone to.
3738
- This is required if O(state=present), O(state=absent), O(state=enabled), or O(state=disabled).
3839
type: str
3940
resource_type:
@@ -95,6 +96,18 @@
9596
- Options to associate with resource action.
9697
type: list
9798
elements: str
99+
resource_clone_ids:
100+
description:
101+
- List of clone resource IDs to clone from.
102+
type: list
103+
elements: str
104+
version_added: 11.3.0
105+
resource_clone_meta:
106+
description:
107+
- List of metadata to associate with clone resource.
108+
type: list
109+
elements: str
110+
version_added: 11.3.0
98111
wait:
99112
description:
100113
- Timeout period for polling the resource creation.
@@ -142,7 +155,7 @@ class PacemakerResource(StateModuleHelper):
142155
module = dict(
143156
argument_spec=dict(
144157
state=dict(type='str', default='present', choices=[
145-
'present', 'absent', 'enabled', 'disabled', 'cleanup']),
158+
'present', 'absent', 'cloned', 'enabled', 'disabled', 'cleanup']),
146159
name=dict(type='str'),
147160
resource_type=dict(type='dict', options=dict(
148161
resource_name=dict(type='str'),
@@ -159,6 +172,8 @@ class PacemakerResource(StateModuleHelper):
159172
argument_action=dict(type='str', choices=['clone', 'master', 'group', 'promotable']),
160173
argument_option=dict(type='list', elements='str'),
161174
)),
175+
resource_clone_ids=dict(type='list', elements='str'),
176+
resource_clone_meta=dict(type='list', elements='str'),
162177
wait=dict(type='int', default=300),
163178
),
164179
required_if=[
@@ -194,17 +209,30 @@ def _get(self):
194209
('out', result[1] if result[1] != "" else None),
195210
('err', result[2])])
196211

212+
def fmt_as_stack_argument(self, value, arg):
213+
if value is not None:
214+
return [x for k in value for x in (arg, k)]
215+
197216
def state_absent(self):
198217
force = get_pacemaker_maintenance_mode(self.runner)
199218
with self.runner('cli_action state name force', output_process=self._process_command_output(True, "does not exist"), check_mode_skip=True) as ctx:
200219
ctx.run(cli_action='resource', force=force)
201220

202221
def state_present(self):
203222
with self.runner(
204-
'cli_action state name resource_type resource_option resource_operation resource_meta resource_argument wait',
223+
'cli_action state name resource_type resource_option resource_operation resource_meta resource_argument '
224+
'resource_clone_ids resource_clone_meta wait',
205225
output_process=self._process_command_output(not get_pacemaker_maintenance_mode(self.runner), "already exists"),
206226
check_mode_skip=True) as ctx:
207-
ctx.run(cli_action='resource')
227+
ctx.run(cli_action='resource', resource_clone_ids=self.fmt_as_stack_argument(self.module.params["resource_clone_ids"], "clone"))
228+
229+
def state_cloned(self):
230+
with self.runner(
231+
'cli_action state name resource_clone_ids resource_clone_meta wait',
232+
output_process=self._process_command_output(
233+
not get_pacemaker_maintenance_mode(self.runner),
234+
"already a clone resource"), check_mode_skip=True) as ctx:
235+
ctx.run(cli_action='resource', resource_clone_meta=self.fmt_as_stack_argument(self.module.params["resource_clone_meta"], "meta"))
208236

209237
def state_enabled(self):
210238
with self.runner('cli_action state name', output_process=self._process_command_output(True, "Starting"), check_mode_skip=True) as ctx:

tests/unit/plugins/modules/test_pacemaker_resource.yaml

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ test_cases:
7878
argument_action: group
7979
argument_option:
8080
- test_group
81+
resource_clone_ids:
82+
- test_clone
83+
resource_clone_meta:
84+
- test_clone_meta1=789
8185
wait: 200
8286
output:
8387
changed: true
@@ -100,7 +104,7 @@ test_cases:
100104
dc-version=2.1.9-1.fc41-7188dbf
101105
have-watchdog=false
102106
err: ""
103-
- command: [/testbin/pcs, resource, create, virtual-ip, ocf:heartbeat:IPaddr2, "ip=[192.168.2.1]", op, start, timeout=1200, op, stop, timeout=1200, op, monitor, timeout=1200, meta, test_meta1=123, meta, test_meta2=456, --group, test_group, --wait=200]
107+
- command: [/testbin/pcs, resource, create, virtual-ip, ocf:heartbeat:IPaddr2, "ip=[192.168.2.1]", op, start, timeout=1200, op, stop, timeout=1200, op, monitor, timeout=1200, meta, test_meta1=123, meta, test_meta2=456, --group, test_group, clone, test_clone, test_clone_meta1=789, --wait=200]
104108
environ: *env-def
105109
rc: 0
106110
out: "Assumed agent name 'ocf:heartbeat:IPaddr2'"
@@ -496,3 +500,72 @@ test_cases:
496500
rc: 0
497501
out: "NO resources configured"
498502
err: ""
503+
- id: test_clone_minimal_input_resource_not_exist
504+
input:
505+
state: cloned
506+
name: virtual-ip
507+
output:
508+
failed: true
509+
msg: "pcs failed with error (rc=1): Error: unable to find group or resource: virtual-ip"
510+
mocks:
511+
run_command:
512+
- command: [/testbin/pcs, resource, status, virtual-ip]
513+
environ: *env-def
514+
rc: 1
515+
out: ""
516+
err: "Error: resource or tag id 'virtual-ip' not found"
517+
- command: [/testbin/pcs, property, config]
518+
environ: *env-def
519+
rc: 1
520+
out: |
521+
Cluster Properties: cib-bootstrap-options
522+
cluster-infrastructure=corosync
523+
cluster-name=hacluster
524+
dc-version=2.1.9-1.fc41-7188dbf
525+
have-watchdog=false
526+
err: ""
527+
- command: [/testbin/pcs, resource, clone, virtual-ip, --wait=300]
528+
environ: *env-def
529+
rc: 1
530+
out: ""
531+
err: "Error: unable to find group or resource: virtual-ip"
532+
- id: test_clone_filled_input_resource_exists
533+
input:
534+
state: cloned
535+
name: virtual-ip
536+
resource_clone_ids:
537+
- test_clone
538+
resource_clone_meta:
539+
- test_clone_meta1=789
540+
wait: 200
541+
output:
542+
changed: true
543+
previous_value: " * virtual-ip\t(ocf:heartbeat:IPAddr2):\t Started"
544+
value: " * Clone Set: virtual-ip-clone [virtual-ip]\t(ocf:heartbeat:IPAddr2):\t Started"
545+
mocks:
546+
run_command:
547+
- command: [/testbin/pcs, resource, status, virtual-ip]
548+
environ: *env-def
549+
rc: 0
550+
out: " * virtual-ip\t(ocf:heartbeat:IPAddr2):\t Started"
551+
err: ""
552+
- command: [/testbin/pcs, property, config]
553+
environ: *env-def
554+
rc: 1
555+
out: |
556+
Cluster Properties: cib-bootstrap-options
557+
cluster-infrastructure=corosync
558+
cluster-name=hacluster
559+
dc-version=2.1.9-1.fc41-7188dbf
560+
have-watchdog=false
561+
err: ""
562+
- command: [/testbin/pcs, resource, clone, virtual-ip, test_clone, meta, test_clone_meta1=789, --wait=200]
563+
environ: *env-def
564+
rc: 0
565+
out: ""
566+
err: ""
567+
- command: [/testbin/pcs, resource, status, virtual-ip]
568+
environ: *env-def
569+
rc: 0
570+
out: " * Clone Set: virtual-ip-clone [virtual-ip]\t(ocf:heartbeat:IPAddr2):\t Started"
571+
err: ""

0 commit comments

Comments
 (0)