2020-03-09 10:11:07 +01:00
|
|
|
#!/usr/bin/python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
|
|
|
# Copyright 2015 Nandaja Varma <nvarma@redhat.com>
|
|
|
|
# Copyright 2018 Red Hat, Inc.
|
|
|
|
#
|
|
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
#
|
|
|
|
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
__metaclass__ = type
|
|
|
|
|
|
|
|
DOCUMENTATION = '''
|
|
|
|
---
|
|
|
|
module: gluster_peer
|
|
|
|
short_description: Attach/Detach peers to/from the cluster
|
|
|
|
description:
|
|
|
|
- Create or diminish a GlusterFS trusted storage pool. A set of nodes can be
|
|
|
|
added into an existing trusted storage pool or a new storage pool can be
|
|
|
|
formed. Or, nodes can be removed from an existing trusted storage pool.
|
|
|
|
author: Sachidananda Urs (@sac)
|
|
|
|
options:
|
|
|
|
state:
|
2020-11-28 04:54:00 +01:00
|
|
|
choices: ["present", "absent"]
|
|
|
|
default: "present"
|
|
|
|
description:
|
2020-03-09 10:11:07 +01:00
|
|
|
- Determines whether the nodes should be attached to the pool or
|
|
|
|
removed from the pool. If the state is present, nodes will be
|
|
|
|
attached to the pool. If state is absent, nodes will be detached
|
|
|
|
from the pool.
|
2020-11-28 04:54:00 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
nodes:
|
2020-11-28 04:54:00 +01:00
|
|
|
description:
|
2020-03-09 10:11:07 +01:00
|
|
|
- List of nodes that have to be probed into the pool.
|
2020-11-28 04:54:00 +01:00
|
|
|
required: true
|
|
|
|
type: list
|
2020-03-09 10:11:07 +01:00
|
|
|
force:
|
2020-11-28 04:54:00 +01:00
|
|
|
type: bool
|
|
|
|
default: false
|
|
|
|
description:
|
2020-03-09 10:11:07 +01:00
|
|
|
- Applicable only while removing the nodes from the pool. gluster
|
|
|
|
will refuse to detach a node from the pool if any one of the node
|
|
|
|
is down, in such cases force can be used.
|
|
|
|
requirements:
|
|
|
|
- GlusterFS > 3.2
|
|
|
|
notes:
|
|
|
|
- This module does not support check mode.
|
|
|
|
'''
|
|
|
|
|
|
|
|
EXAMPLES = '''
|
|
|
|
- name: Create a trusted storage pool
|
2020-07-13 21:50:31 +02:00
|
|
|
community.general.gluster_peer:
|
2020-03-09 10:11:07 +01:00
|
|
|
state: present
|
|
|
|
nodes:
|
|
|
|
- 10.0.1.5
|
|
|
|
- 10.0.1.10
|
|
|
|
|
|
|
|
- name: Delete a node from the trusted storage pool
|
2020-07-13 21:50:31 +02:00
|
|
|
community.general.gluster_peer:
|
2020-03-09 10:11:07 +01:00
|
|
|
state: absent
|
|
|
|
nodes:
|
|
|
|
- 10.0.1.10
|
|
|
|
|
|
|
|
- name: Delete a node from the trusted storage pool by force
|
2020-07-13 21:50:31 +02:00
|
|
|
community.general.gluster_peer:
|
2020-03-09 10:11:07 +01:00
|
|
|
state: absent
|
|
|
|
nodes:
|
|
|
|
- 10.0.0.1
|
|
|
|
force: true
|
|
|
|
'''
|
|
|
|
|
|
|
|
RETURN = '''
|
|
|
|
'''
|
|
|
|
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
from distutils.version import LooseVersion
|
|
|
|
|
|
|
|
|
|
|
|
class Peer(object):
|
|
|
|
def __init__(self, module):
|
|
|
|
self.module = module
|
|
|
|
self.state = self.module.params['state']
|
|
|
|
self.nodes = self.module.params['nodes']
|
|
|
|
self.glustercmd = self.module.get_bin_path('gluster', True)
|
|
|
|
self.lang = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C')
|
|
|
|
self.action = ''
|
|
|
|
self.force = ''
|
|
|
|
|
|
|
|
def gluster_peer_ops(self):
|
|
|
|
if not self.nodes:
|
|
|
|
self.module.fail_json(msg="nodes list cannot be empty")
|
|
|
|
self.force = 'force' if self.module.params.get('force') else ''
|
|
|
|
if self.state == 'present':
|
|
|
|
self.nodes = self.get_to_be_probed_hosts(self.nodes)
|
|
|
|
self.action = 'probe'
|
|
|
|
# In case of peer probe, we do not need `force'
|
|
|
|
self.force = ''
|
|
|
|
else:
|
|
|
|
self.action = 'detach'
|
|
|
|
self.call_peer_commands()
|
|
|
|
|
|
|
|
def get_to_be_probed_hosts(self, hosts):
|
|
|
|
peercmd = [self.glustercmd, 'pool', 'list', '--mode=script']
|
|
|
|
rc, output, err = self.module.run_command(peercmd,
|
|
|
|
environ_update=self.lang)
|
|
|
|
peers_in_cluster = [line.split('\t')[1].strip() for
|
|
|
|
line in filter(None, output.split('\n')[1:])]
|
|
|
|
try:
|
|
|
|
peers_in_cluster.remove('localhost')
|
|
|
|
except ValueError:
|
|
|
|
# It is ok not to have localhost in list
|
|
|
|
pass
|
|
|
|
hosts_to_be_probed = [host for host in hosts if host not in
|
|
|
|
peers_in_cluster]
|
|
|
|
return hosts_to_be_probed
|
|
|
|
|
|
|
|
def call_peer_commands(self):
|
|
|
|
result = {}
|
|
|
|
result['msg'] = ''
|
|
|
|
result['changed'] = False
|
|
|
|
|
|
|
|
for node in self.nodes:
|
|
|
|
peercmd = [self.glustercmd, 'peer', self.action, node, '--mode=script']
|
|
|
|
if self.force:
|
|
|
|
peercmd.append(self.force)
|
|
|
|
rc, out, err = self.module.run_command(peercmd,
|
|
|
|
environ_update=self.lang)
|
|
|
|
if rc:
|
|
|
|
result['rc'] = rc
|
|
|
|
result['msg'] = err
|
|
|
|
# Fail early, do not wait for the loop to finish
|
|
|
|
self.module.fail_json(**result)
|
|
|
|
else:
|
|
|
|
if 'already in peer' in out or \
|
|
|
|
'localhost not needed' in out:
|
|
|
|
result['changed'] |= False
|
|
|
|
else:
|
|
|
|
result['changed'] = True
|
|
|
|
self.module.exit_json(**result)
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
module = AnsibleModule(
|
|
|
|
argument_spec=dict(
|
2020-11-23 12:14:43 +01:00
|
|
|
force=dict(type='bool', required=False, default=False),
|
2020-03-09 10:11:07 +01:00
|
|
|
nodes=dict(type='list', required=True),
|
|
|
|
state=dict(type='str', choices=['absent', 'present'],
|
|
|
|
default='present'),
|
|
|
|
),
|
|
|
|
supports_check_mode=False
|
|
|
|
)
|
|
|
|
pops = Peer(module)
|
|
|
|
required_version = "3.2"
|
|
|
|
# Verify if required GlusterFS version is installed
|
|
|
|
if is_invalid_gluster_version(module, required_version):
|
|
|
|
module.fail_json(msg="GlusterFS version > %s is required" %
|
|
|
|
required_version)
|
|
|
|
pops.gluster_peer_ops()
|
|
|
|
|
|
|
|
|
|
|
|
def is_invalid_gluster_version(module, required_version):
|
|
|
|
cmd = module.get_bin_path('gluster', True) + ' --version'
|
|
|
|
result = module.run_command(cmd)
|
|
|
|
ver_line = result[1].split('\n')[0]
|
|
|
|
version = ver_line.split(' ')[1]
|
|
|
|
# If the installed version is less than 3.2, it is an invalid version
|
|
|
|
# return True
|
|
|
|
return LooseVersion(version) < LooseVersion(required_version)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|