mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-06-28 03:00:23 -07:00
* feat(maintenance): Add cluster maintenance mode checks for pacemaker * bug(fix): Correct substring typo and unit test This commit corrects a substring check for determining if the pacemaker cluster is in maintenance mode. Additionally, unit test is corrected with correct output from pacemaker when in maintenance mode. * feat(maintenance): Add force argument for absent resources This commit adds in --force argument for resources intended to be absent within a cluster that is in maintenance mode. Without this argument, the cluster will not attempt to remove the resource due to maintenance mode. The resource will be declared as orphaned and exiting maintenance mode will allow the cluster to remove the resource completely. * refactor(review): Apply code review changes This commit adds refactors to enhance code quality. * doc(changelog): Add fragment for maintenance mode addition
230 lines
7.6 KiB
Python
230 lines
7.6 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright (c) 2025, Dexter Le <dextersydney2001@gmail.com>
|
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
__metaclass__ = type
|
|
|
|
DOCUMENTATION = '''
|
|
---
|
|
module: pacemaker_resource
|
|
short_description: Manage pacemaker resources
|
|
author:
|
|
- Dexter Le (@munchtoast)
|
|
version_added: 10.5.0
|
|
description:
|
|
- This module can manage resources in a Pacemaker cluster using the pacemaker CLI.
|
|
extends_documentation_fragment:
|
|
- community.general.attributes
|
|
attributes:
|
|
check_mode:
|
|
support: full
|
|
diff_mode:
|
|
support: none
|
|
options:
|
|
state:
|
|
description:
|
|
- Indicate desired state for cluster resource.
|
|
choices: [ present, absent, enabled, disabled ]
|
|
default: present
|
|
type: str
|
|
name:
|
|
description:
|
|
- Specify the resource name to create.
|
|
required: true
|
|
type: str
|
|
resource_type:
|
|
description:
|
|
- Resource type to create.
|
|
type: dict
|
|
suboptions:
|
|
resource_name:
|
|
description:
|
|
- Specify the resource type name.
|
|
type: str
|
|
resource_standard:
|
|
description:
|
|
- Specify the resource type standard.
|
|
type: str
|
|
resource_provider:
|
|
description:
|
|
- Specify the resource type providers.
|
|
type: str
|
|
resource_option:
|
|
description:
|
|
- Specify the resource option to create.
|
|
type: list
|
|
elements: str
|
|
default: []
|
|
resource_operation:
|
|
description:
|
|
- List of operations to associate with resource.
|
|
type: list
|
|
elements: dict
|
|
default: []
|
|
suboptions:
|
|
operation_action:
|
|
description:
|
|
- Operation action to associate with resource.
|
|
type: str
|
|
operation_option:
|
|
description:
|
|
- Operation option to associate with action.
|
|
type: list
|
|
elements: str
|
|
resource_meta:
|
|
description:
|
|
- List of meta to associate with resource.
|
|
type: list
|
|
elements: str
|
|
resource_argument:
|
|
description:
|
|
- Action to associate with resource.
|
|
type: dict
|
|
suboptions:
|
|
argument_action:
|
|
description:
|
|
- Action to apply to resource.
|
|
type: str
|
|
choices: [ clone, master, group, promotable ]
|
|
argument_option:
|
|
description:
|
|
- Options to associate with resource action.
|
|
type: list
|
|
elements: str
|
|
wait:
|
|
description:
|
|
- Timeout period for polling the resource creation.
|
|
type: int
|
|
default: 300
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
---
|
|
- name: Create pacemaker resource
|
|
hosts: localhost
|
|
gather_facts: false
|
|
tasks:
|
|
- name: Create virtual-ip resource
|
|
community.general.pacemaker_resource:
|
|
state: present
|
|
name: virtual-ip
|
|
resource_type:
|
|
resource_name: IPaddr2
|
|
resource_option:
|
|
- "ip=[192.168.2.1]"
|
|
resource_argument:
|
|
argument_action: group
|
|
argument_option:
|
|
- master
|
|
resource_operation:
|
|
- operation_action: monitor
|
|
operation_option:
|
|
- interval=20
|
|
'''
|
|
|
|
RETURN = '''
|
|
cluster_resources:
|
|
description: The cluster resource output message.
|
|
type: str
|
|
sample: "Assumed agent name ocf:heartbeat:IPaddr2 (deduced from IPaddr2)"
|
|
returned: always
|
|
'''
|
|
|
|
from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper
|
|
from ansible_collections.community.general.plugins.module_utils.pacemaker import pacemaker_runner, get_pacemaker_maintenance_mode
|
|
|
|
|
|
class PacemakerResource(StateModuleHelper):
|
|
module = dict(
|
|
argument_spec=dict(
|
|
state=dict(type='str', default='present', choices=[
|
|
'present', 'absent', 'enabled', 'disabled']),
|
|
name=dict(type='str', required=True),
|
|
resource_type=dict(type='dict', options=dict(
|
|
resource_name=dict(type='str'),
|
|
resource_standard=dict(type='str'),
|
|
resource_provider=dict(type='str'),
|
|
)),
|
|
resource_option=dict(type='list', elements='str', default=list()),
|
|
resource_operation=dict(type='list', elements='dict', default=list(), options=dict(
|
|
operation_action=dict(type='str'),
|
|
operation_option=dict(type='list', elements='str'),
|
|
)),
|
|
resource_meta=dict(type='list', elements='str'),
|
|
resource_argument=dict(type='dict', options=dict(
|
|
argument_action=dict(type='str', choices=['clone', 'master', 'group', 'promotable']),
|
|
argument_option=dict(type='list', elements='str'),
|
|
)),
|
|
wait=dict(type='int', default=300),
|
|
),
|
|
required_if=[('state', 'present', ['resource_type', 'resource_option'])],
|
|
supports_check_mode=True,
|
|
)
|
|
default_state = "present"
|
|
|
|
def __init_module__(self):
|
|
self.runner = pacemaker_runner(self.module, cli_action='resource')
|
|
self._maintenance_mode_runner = pacemaker_runner(self.module, cli_action='property')
|
|
self.vars.set('previous_value', self._get())
|
|
self.vars.set('value', self.vars.previous_value, change=True, diff=True)
|
|
|
|
def _process_command_output(self, fail_on_err, ignore_err_msg=""):
|
|
def process(rc, out, err):
|
|
if fail_on_err and rc != 0 and err and ignore_err_msg not in err:
|
|
self.do_raise('pcs failed with error (rc={0}): {1}'.format(rc, err))
|
|
out = out.rstrip()
|
|
return None if out == "" else out
|
|
return process
|
|
|
|
def _get(self):
|
|
with self.runner('state name', output_process=self._process_command_output(False)) as ctx:
|
|
return ctx.run(state='status')
|
|
|
|
def state_absent(self):
|
|
runner_args = ['state', 'name', 'force']
|
|
force = get_pacemaker_maintenance_mode(self._maintenance_mode_runner)
|
|
with self.runner(runner_args, output_process=self._process_command_output(True, "does not exist"), check_mode_skip=True) as ctx:
|
|
ctx.run(force=force)
|
|
self.vars.set('value', self._get())
|
|
self.vars.stdout = ctx.results_out
|
|
self.vars.stderr = ctx.results_err
|
|
self.vars.cmd = ctx.cmd
|
|
|
|
def state_present(self):
|
|
with self.runner(
|
|
'state name resource_type resource_option resource_operation resource_meta resource_argument wait',
|
|
output_process=self._process_command_output(not get_pacemaker_maintenance_mode(self._maintenance_mode_runner), "already exists"),
|
|
check_mode_skip=True) as ctx:
|
|
ctx.run()
|
|
self.vars.set('value', self._get())
|
|
self.vars.stdout = ctx.results_out
|
|
self.vars.stderr = ctx.results_err
|
|
self.vars.cmd = ctx.cmd
|
|
|
|
def state_enabled(self):
|
|
with self.runner('state name', output_process=self._process_command_output(True, "Starting"), check_mode_skip=True) as ctx:
|
|
ctx.run()
|
|
self.vars.set('value', self._get())
|
|
self.vars.stdout = ctx.results_out
|
|
self.vars.stderr = ctx.results_err
|
|
self.vars.cmd = ctx.cmd
|
|
|
|
def state_disabled(self):
|
|
with self.runner('state name', output_process=self._process_command_output(True, "Stopped"), check_mode_skip=True) as ctx:
|
|
ctx.run()
|
|
self.vars.set('value', self._get())
|
|
self.vars.stdout = ctx.results_out
|
|
self.vars.stderr = ctx.results_err
|
|
self.vars.cmd = ctx.cmd
|
|
|
|
|
|
def main():
|
|
PacemakerResource.execute()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|