mirror of https://github.com/ansible/ansible.git
gluster_peer: Module to create/dismantle trusted storage pool (#37771)
* gluster_peer: Module to create/dismantle trusted storage pool * gluster_peer: Module to create/dismantle trusted storage pool Added __init__.py and added function main() in the module. * gluster_peer: Module to create/dismantle trusted storage pool Empty __init__.py was needed. Removed comment from the file. * gluster_peer: Module to create/dismantle trusted storage pool Addressed review comments. * gluster_peer: Module to create/dismantle trusted storage pool Do version check more sanely, improve parameter handling. * gluster_peer: Module to create/dismantle trusted storage pool Use isinstance for type comparison. * gluster_peer: Module to create/dismantle trusted storage pool Use type=list for nodes parameter, get rid of literal_eval. * gluster_peer: Module to create/dismantle trusted storage pool Add parameter check_mode, had missed this somewhere in between. * gluster_peer: do not set `force' in case of state=present * gluster_peer: Fix typo in the documentation * gluster_peer: make peer probe idempotent * gluster_peer: Fix a logical error while evaluating booleans * gluster_peer: set locale to C, pass list to run_commandpull/33986/merge
parent
9f732d2f05
commit
5861d5f74e
@ -0,0 +1,176 @@
|
||||
#!/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
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
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.
|
||||
version_added: "2.6"
|
||||
author: Sachidananda Urs (@sac)
|
||||
options:
|
||||
state:
|
||||
choices: ["present", "absent"]
|
||||
default: "present"
|
||||
description:
|
||||
- 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.
|
||||
required: true
|
||||
nodes:
|
||||
description:
|
||||
- List of nodes that have to be probed into the pool.
|
||||
required: true
|
||||
force:
|
||||
type: bool
|
||||
default: "false"
|
||||
description:
|
||||
- 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
|
||||
gluster_peer:
|
||||
state: present
|
||||
nodes:
|
||||
- 10.0.1.5
|
||||
- 10.0.1.10
|
||||
|
||||
- name: Delete a node from the trusted storage pool
|
||||
gluster_peer:
|
||||
state: absent
|
||||
nodes:
|
||||
- 10.0.1.10
|
||||
|
||||
- name: Delete a node from the trusted storage pool by force
|
||||
gluster_peer:
|
||||
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']
|
||||
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]
|
||||
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(
|
||||
force=dict(type='bool', required=False),
|
||||
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()
|
Loading…
Reference in New Issue