PEP8 fixes: Ansible system module and playbook base.py (#32322)

* Ansible files module sanity pep8 fixes

* Ansible system module and playbook base.py

* Undo empty lines not required by sanity checks

* Undo empty lines not required by sanity checks

* Undo empty lines not required by sanity checks

* Undo empty lines not required by sanity checks

* Undo empty lines not required by sanity checks

* Undo empty lines not required by sanity checks

* Undo empty lines not required by sanity checks

* Undo empty lines not required by sanity checks

* Undo empty lines not required by sanity checks

* Undo empty lines not required by sanity checks

* Undo empty lines not required by sanity checks

* Various changes

* Various changes

* Various changes

* Various changes

* Undo blank lines not required by sanity checks

* Various changes

* Various changes

* Various changes

* Various changes

* Various changes

* Undo blank line changes not required by sanity checks

* Various changes

* Various changes

* Various changes

* Various changes

* Various changes

* Missing piece after merge

* Blank lines

* Blank line

* Line too long

* Fix typo

* Unnecessary quotes

* Fix example error
pull/32370/merge
Yadnyawalkya Tale 7 years ago committed by Dag Wieers
parent a5da2e44a1
commit a2d34e914e

@ -181,15 +181,15 @@ def check_file_attrs(module, changed, message, diff):
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
path=dict(required=True, aliases=['dest', 'destfile', 'name'], type='path'), path=dict(type='path', required=True, aliases=['dest', 'destfile', 'name']),
state=dict(default='present', choices=['absent', 'present']), state=dict(type='str', default='present', choices=['absent', 'present']),
marker=dict(default='# {mark} ANSIBLE MANAGED BLOCK', type='str'), marker=dict(type='str', default='# {mark} ANSIBLE MANAGED BLOCK'),
block=dict(default='', type='str', aliases=['content']), block=dict(type='str', default='', aliases=['content']),
insertafter=dict(default=None), insertafter=dict(type='str'),
insertbefore=dict(default=None), insertbefore=dict(type='str'),
create=dict(default=False, type='bool'), create=dict(type='bool', default=False),
backup=dict(default=False, type='bool'), backup=dict(type='bool', default=False),
validate=dict(default=None, type='str'), validate=dict(type='str'),
), ),
mutually_exclusive=[['insertbefore', 'insertafter']], mutually_exclusive=[['insertbefore', 'insertafter']],
add_file_common_args=True, add_file_common_args=True,
@ -271,7 +271,7 @@ def main():
elif insertafter is not None: elif insertafter is not None:
n0 += 1 n0 += 1
elif insertbefore is not None: elif insertbefore is not None:
n0 = 0 # insertbefore=BOF n0 = 0 # insertbefore=BOF
else: else:
n0 = len(lines) # insertafter=EOF n0 = len(lines) # insertafter=EOF
elif n0 < n1: elif n0 < n1:

@ -331,8 +331,8 @@ def is_rsh_needed(source, dest):
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
src=dict(required=True), src=dict(type='str', required=True),
dest=dict(required=True), dest=dict(type='str', required=True),
dest_port=dict(type='int'), dest_port=dict(type='int'),
delete=dict(type='bool', default=False), delete=dict(type='bool', default=False),
private_key=dict(type='path'), private_key=dict(type='path'),

@ -1,25 +1,22 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2014, Brian Coca <briancoca+ansible@gmail.com> # Copyright: (c) 2014, Brian Coca <briancoca+ansible@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'], 'status': ['stableinterface'],
'supported_by': 'core'} 'supported_by': 'core'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: debconf module: debconf
short_description: Configure a .deb package short_description: Configure a .deb package
description: description:
- Configure a .deb package using debconf-set-selections. Or just query - Configure a .deb package using debconf-set-selections. Or just query existing selections.
existing selections.
version_added: "1.6" version_added: "1.6"
notes: notes:
- This module requires the command line debconf tools. - This module requires the command line debconf tools.
@ -33,60 +30,54 @@ options:
description: description:
- Name of package to configure. - Name of package to configure.
required: true required: true
default: null aliases: [ pkg ]
aliases: ['pkg']
question: question:
description: description:
- A debconf configuration setting - A debconf configuration setting.
required: false aliases: [ selection, setting ]
default: null
aliases: ['setting', 'selection']
vtype: vtype:
description: description:
- The type of the value supplied. - The type of the value supplied.
- C(seen) was added in 2.2. - C(seen) was added in 2.2.
required: false choices: [ boolean, error, multiselect, note, password, seen, select, string, text, title, text ]
default: null
choices: [string, password, boolean, select, multiselect, note, error, title, text, seen]
value: value:
description: description:
- Value to set the configuration to - Value to set the configuration to.
required: false aliases: [ answer ]
default: null
aliases: ['answer']
unseen: unseen:
description: description:
- Do not set 'seen' flag when pre-seeding - Do not set 'seen' flag when pre-seeding.
required: false type: bool
default: False default: False
author: "Brian Coca (@bcoca)" author:
- Brian Coca (@bcoca)
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Set default locale to fr_FR.UTF-8 - name: Set default locale to fr_FR.UTF-8
- debconf: debconf:
name: locales name: locales
question: locales/default_environment_locale question: locales/default_environment_locale
value: fr_FR.UTF-8 value: fr_FR.UTF-8
vtype: select vtype: select
# set to generate locales: - name: set to generate locales
- debconf: debconf:
name: locales name: locales
question: locales/locales_to_be_generated question: locales/locales_to_be_generated
value: en_US.UTF-8 UTF-8, fr_FR.UTF-8 UTF-8 value: en_US.UTF-8 UTF-8, fr_FR.UTF-8 UTF-8
vtype: multiselect vtype: multiselect
# Accept oracle license - name: Accept oracle license
- debconf: debconf:
name: oracle-java7-installer name: oracle-java7-installer
question: shared/accepted-oracle-license-v1-1 question: shared/accepted-oracle-license-v1-1
value: true value: true
vtype: select vtype: select
# Specifying package you can register/return the list of questions and current values - name: Specifying package you can register/return the list of questions and current values
- debconf: debconf:
name: tzdata name: tzdata
''' '''
@ -104,13 +95,12 @@ def get_selections(module, pkg):
for line in out.splitlines(): for line in out.splitlines():
(key, value) = line.split(':', 1) (key, value) = line.split(':', 1)
selections[ key.strip('*').strip() ] = value.strip() selections[key.strip('*').strip()] = value.strip()
return selections return selections
def set_selection(module, pkg, question, vtype, value, unseen): def set_selection(module, pkg, question, vtype, value, unseen):
setsel = module.get_bin_path('debconf-set-selections', True) setsel = module.get_bin_path('debconf-set-selections', True)
cmd = [setsel] cmd = [setsel]
if unseen: if unseen:
@ -125,27 +115,26 @@ def set_selection(module, pkg, question, vtype, value, unseen):
return module.run_command(cmd, data=data) return module.run_command(cmd, data=data)
def main():
def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
name=dict(required=True, aliases=['pkg'], type='str'), name=dict(type='str', required=True, aliases=['pkg']),
question=dict(required=False, aliases=['setting', 'selection'], type='str'), question=dict(type='str', aliases=['selection', 'setting']),
vtype=dict(required=False, type='str', choices=['string', 'password', 'boolean', 'select', 'multiselect', 'note', 'error', 'title', vtype=dict(type='str', choices=['boolean', 'error', 'multiselect', 'note', 'password', 'seen', 'select', 'string', 'text', 'title']),
'text', 'seen']), value=dict(type='str', aliases=['answer']),
value=dict(required=False, type='str', aliases=['answer']), unseen=dict(type='bool'),
unseen=dict(required=False, type='bool'),
), ),
required_together=(['question','vtype', 'value'],), required_together=(['question', 'vtype', 'value'],),
supports_check_mode=True, supports_check_mode=True,
) )
#TODO: enable passing array of options and/or debconf file from get-selections dump # TODO: enable passing array of options and/or debconf file from get-selections dump
pkg = module.params["name"] pkg = module.params["name"]
question = module.params["question"] question = module.params["question"]
vtype = module.params["vtype"] vtype = module.params["vtype"]
value = module.params["value"] value = module.params["value"]
unseen = module.params["unseen"] unseen = module.params["unseen"]
prev = get_selections(module, pkg) prev = get_selections(module, pkg)
@ -156,7 +145,7 @@ def main():
if vtype is None or value is None: if vtype is None or value is None:
module.fail_json(msg="when supplying a question you must supply a valid vtype and value") module.fail_json(msg="when supplying a question you must supply a valid vtype and value")
if not question in prev or prev[question] != value: if question not in prev or prev[question] != value:
changed = True changed = True
if changed: if changed:
@ -165,7 +154,7 @@ def main():
if rc: if rc:
module.fail_json(msg=e) module.fail_json(msg=e)
curr = { question: value } curr = {question: value}
if question in prev: if question in prev:
prev = {question: prev[question]} prev = {question: prev[question]}
else: else:

@ -1,33 +1,31 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com> # Copyright: (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: facter module: facter
short_description: Runs the discovery program I(facter) on the remote system short_description: Runs the discovery program I(facter) on the remote system
description: description:
- Runs the I(facter) discovery program - Runs the I(facter) discovery program
(U(https://github.com/puppetlabs/facter)) on the remote system, returning (U(https://github.com/puppetlabs/facter)) on the remote system, returning
JSON data that can be useful for inventory purposes. JSON data that can be useful for inventory purposes.
version_added: "0.2" version_added: "0.2"
options: {} requirements:
notes: [] - facter
requirements: [ "facter", "ruby-json" ] - ruby-json
author: author:
- "Ansible Core Team" - Ansible Core Team
- "Michael DeHaan" - Michael DeHaan
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -41,7 +39,7 @@ from ansible.module_utils.basic import AnsibleModule
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict() argument_spec=dict()
) )
facter_path = module.get_bin_path('facter', opt_dirs=['/opt/puppetlabs/bin']) facter_path = module.get_bin_path('facter', opt_dirs=['/opt/puppetlabs/bin'])

@ -1,21 +1,20 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2013, Alexander Bulimov <lazywolf0@gmail.com> # Copyright: (c) 2013, Alexander Bulimov <lazywolf0@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
author: "Alexander Bulimov (@abulimov)" author:
- Alexander Bulimov (@abulimov)
module: filesystem module: filesystem
short_description: Makes file system on block device short_description: Makes file system on block device
description: description:
@ -23,48 +22,48 @@ description:
version_added: "1.2" version_added: "1.2"
options: options:
fstype: fstype:
choices: [ "ext4", "ext4dev", "ext3", "ext2", "xfs", "btrfs", "reiserfs", "lvm"] choices: [ btrfs, ext2, ext3, ext4, ext4dev, lvm, reiserfs, xfs ]
description: description:
- File System type to be created. - File System type to be created.
- reiserfs support was added in 2.2. - reiserfs support was added in 2.2.
- lvm support was added in 2.5. - lvm support was added in 2.5.
required: true required: yes
dev: dev:
description: description:
- Target block device. - Target block device.
required: true required: yes
force: force:
choices: [ "yes", "no" ]
default: "no"
description: description:
- If yes, allows to create new filesystem on devices that already has filesystem. - If C(yes), allows to create new filesystem on devices that already has filesystem.
required: false type: bool
default: 'no'
resizefs: resizefs:
choices: [ "yes", "no" ]
default: "no"
description: description:
- If yes, if the block device and filessytem size differ, grow the filesystem into the space. Note, XFS Will only grow if mounted. - If C(yes), if the block device and filessytem size differ, grow the filesystem into the space.
required: false - Note, XFS Will only grow if mounted.
type: bool
default: 'no'
version_added: "2.0" version_added: "2.0"
opts: opts:
description: description:
- List of options to be passed to mkfs command. - List of options to be passed to mkfs command.
notes: notes:
- uses mkfs command - Uses mkfs command.
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Create a ext2 filesystem on /dev/sdb1. - name: Create a ext2 filesystem on /dev/sdb1
- filesystem: filesystem:
fstype: ext2 fstype: ext2
dev: /dev/sdb1 dev: /dev/sdb1
# Create a ext4 filesystem on /dev/sdb1 and check disk blocks. - name: Create a ext4 filesystem on /dev/sdb1 and check disk blocks
- filesystem: filesystem:
fstype: ext4 fstype: ext4
dev: /dev/sdb1 dev: /dev/sdb1
opts: -cc opts: -cc
''' '''
import os import os
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
@ -91,7 +90,7 @@ def _get_fs_size(fssize_cmd, dev, module):
block_size = int(line.split(':')[1].strip()) block_size = int(line.split(':')[1].strip())
break break
else: else:
module.fail_json(msg="Failed to get block count and block size of %s with %s" % (dev, cmd), rc=rc, err=err ) module.fail_json(msg="Failed to get block count and block size of %s with %s" % (dev, cmd), rc=rc, err=err)
elif 'xfs_growfs' == fssize_cmd: elif 'xfs_growfs' == fssize_cmd:
# Get Block count and Block size # Get Block count and Block size
rc, size, err = module.run_command([cmd, '-n', dev]) rc, size, err = module.run_command([cmd, '-n', dev])
@ -107,9 +106,9 @@ def _get_fs_size(fssize_cmd, dev, module):
block_count = int(col[3].split(',')[0]) block_count = int(col[3].split(',')[0])
break break
else: else:
module.fail_json(msg="Failed to get block count and block size of %s with %s" % (dev, cmd), rc=rc, err=err ) module.fail_json(msg="Failed to get block count and block size of %s with %s" % (dev, cmd), rc=rc, err=err)
elif 'btrfs' == fssize_cmd: elif 'btrfs' == fssize_cmd:
#ToDo # ToDo
# There is no way to get the blocksize and blockcount for btrfs filesystems # There is no way to get the blocksize and blockcount for btrfs filesystems
block_size = 1 block_size = 1
block_count = 1 block_count = 1
@ -119,9 +118,9 @@ def _get_fs_size(fssize_cmd, dev, module):
block_count = int(size[:-1]) block_count = int(size[:-1])
block_size = 1 block_size = 1
else: else:
module.fail_json(msg="Failed to get block count and block size of %s with %s" % (dev, cmd), rc=rc, err=err ) module.fail_json(msg="Failed to get block count and block size of %s with %s" % (dev, cmd), rc=rc, err=err)
return block_size*block_count return block_size * block_count
def main(): def main():
@ -131,77 +130,76 @@ def main():
# There is no "single command" to manipulate filesystems, so we map them all out and their options # There is no "single command" to manipulate filesystems, so we map them all out and their options
fs_cmd_map = { fs_cmd_map = {
'ext2' : { 'ext2': {
'mkfs' : 'mkfs.ext2', 'mkfs': 'mkfs.ext2',
'grow' : 'resize2fs', 'grow': 'resize2fs',
'grow_flag' : None, 'grow_flag': None,
'force_flag' : '-F', 'force_flag': '-F',
'fsinfo': 'tune2fs', 'fsinfo': 'tune2fs',
}, },
'ext3' : { 'ext3': {
'mkfs' : 'mkfs.ext3', 'mkfs': 'mkfs.ext3',
'grow' : 'resize2fs', 'grow': 'resize2fs',
'grow_flag' : None, 'grow_flag': None,
'force_flag' : '-F', 'force_flag': '-F',
'fsinfo': 'tune2fs', 'fsinfo': 'tune2fs',
}, },
'ext4' : { 'ext4': {
'mkfs' : 'mkfs.ext4', 'mkfs': 'mkfs.ext4',
'grow' : 'resize2fs', 'grow': 'resize2fs',
'grow_flag' : None, 'grow_flag': None,
'force_flag' : '-F', 'force_flag': '-F',
'fsinfo': 'tune2fs', 'fsinfo': 'tune2fs',
}, },
'reiserfs' : { 'reiserfs': {
'mkfs' : 'mkfs.reiserfs', 'mkfs': 'mkfs.reiserfs',
'grow' : 'resize_reiserfs', 'grow': 'resize_reiserfs',
'grow_flag' : None, 'grow_flag': None,
'force_flag' : '-f', 'force_flag': '-f',
'fsinfo': 'reiserfstune', 'fsinfo': 'reiserfstune',
}, },
'ext4dev' : { 'ext4dev': {
'mkfs' : 'mkfs.ext4', 'mkfs': 'mkfs.ext4',
'grow' : 'resize2fs', 'grow': 'resize2fs',
'grow_flag' : None, 'grow_flag': None,
'force_flag' : '-F', 'force_flag': '-F',
'fsinfo': 'tune2fs', 'fsinfo': 'tune2fs',
}, },
'xfs' : { 'xfs': {
'mkfs' : 'mkfs.xfs', 'mkfs': 'mkfs.xfs',
'grow' : 'xfs_growfs', 'grow': 'xfs_growfs',
'grow_flag' : None, 'grow_flag': None,
'force_flag' : '-f', 'force_flag': '-f',
'fsinfo': 'xfs_growfs', 'fsinfo': 'xfs_growfs',
}, },
'btrfs' : { 'btrfs': {
'mkfs' : 'mkfs.btrfs', 'mkfs': 'mkfs.btrfs',
'grow' : 'btrfs', 'grow': 'btrfs',
'grow_flag' : 'filesystem resize', 'grow_flag': 'filesystem resize',
'force_flag' : '-f', 'force_flag': '-f',
'fsinfo': 'btrfs', 'fsinfo': 'btrfs',
}, },
'LVM2_member' : { 'LVM2_member': {
'mkfs' : 'pvcreate', 'mkfs': 'pvcreate',
'grow' : 'pvresize', 'grow': 'pvresize',
'grow_flag' : None, 'grow_flag': None,
'force_flag' : '-f' , 'force_flag': '-f',
'fsinfo': 'pvs', 'fsinfo': 'pvs',
} }
} }
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec=dict(
fstype=dict(required=True, aliases=['type'], fstype=dict(type='str', required=True, aliases=['type'],
choices=fs_cmd_map.keys() + friendly_names.keys()), choices=fs_cmd_map.keys() + friendly_names.keys()),
dev=dict(required=True, aliases=['device']), dev=dict(type='str', required=True, aliases=['device']),
opts=dict(), opts=dict(type='str'),
force=dict(type='bool', default='no'), force=dict(type='bool', default=False),
resizefs=dict(type='bool', default='no'), resizefs=dict(type='bool', default=False),
), ),
supports_check_mode=True, supports_check_mode=True,
) )
dev = module.params['dev'] dev = module.params['dev']
fstype = module.params['fstype'] fstype = module.params['fstype']
opts = module.params['opts'] opts = module.params['opts']
@ -224,11 +222,11 @@ def main():
fssize_cmd = fs_cmd_map[fstype]['fsinfo'] fssize_cmd = fs_cmd_map[fstype]['fsinfo']
if not os.path.exists(dev): if not os.path.exists(dev):
module.fail_json(msg="Device %s not found."%dev) module.fail_json(msg="Device %s not found." % dev)
cmd = module.get_bin_path('blkid', required=True) cmd = module.get_bin_path('blkid', required=True)
rc,raw_fs,err = module.run_command("%s -c /dev/null -o value -s TYPE %s" % (cmd, dev)) rc, raw_fs, err = module.run_command("%s -c /dev/null -o value -s TYPE %s" % (cmd, dev))
fs = raw_fs.strip() fs = raw_fs.strip()
if fs == fstype and resizefs is False and not force: if fs == fstype and resizefs is False and not force:
@ -242,27 +240,26 @@ def main():
else: else:
fs_smaller = False fs_smaller = False
if module.check_mode and fs_smaller: if module.check_mode and fs_smaller:
module.exit_json(changed=True, msg="Resizing filesystem %s on device %s" % (fstype,dev)) module.exit_json(changed=True, msg="Resizing filesystem %s on device %s" % (fstype, dev))
elif module.check_mode and not fs_smaller: elif module.check_mode and not fs_smaller:
module.exit_json(changed=False, msg="%s filesystem is using the whole device %s" % (fstype, dev)) module.exit_json(changed=False, msg="%s filesystem is using the whole device %s" % (fstype, dev))
elif fs_smaller: elif fs_smaller:
cmd = module.get_bin_path(growcmd, required=True) cmd = module.get_bin_path(growcmd, required=True)
rc,out,err = module.run_command("%s %s" % (cmd, dev)) rc, out, err = module.run_command("%s %s" % (cmd, dev))
# Sadly there is no easy way to determine if this has changed. For now, just say "true" and move on. # Sadly there is no easy way to determine if this has changed. For now, just say "true" and move on.
# in the future, you would have to parse the output to determine this. # in the future, you would have to parse the output to determine this.
# thankfully, these are safe operations if no change is made. # thankfully, these are safe operations if no change is made.
if rc == 0: if rc == 0:
module.exit_json(changed=True, msg=out) module.exit_json(changed=True, msg=out)
else: else:
module.fail_json(msg="Resizing filesystem %s on device '%s' failed"%(fstype,dev), rc=rc, err=err) module.fail_json(msg="Resizing filesystem %s on device '%s' failed" % (fstype, dev), rc=rc, err=err)
else: else:
module.exit_json(changed=False, msg="%s filesystem is using the whole device %s" % (fstype, dev)) module.exit_json(changed=False, msg="%s filesystem is using the whole device %s" % (fstype, dev))
elif fs and not force: elif fs and not force:
module.fail_json(msg="'%s' is already used as %s, use force=yes to overwrite"%(dev,fs), rc=rc, err=err) module.fail_json(msg="'%s' is already used as %s, use force=yes to overwrite" % (dev, fs), rc=rc, err=err)
### create fs # create fs
if module.check_mode: if module.check_mode:
changed = True changed = True
@ -274,11 +271,11 @@ def main():
cmd = "%s %s '%s'" % (mkfs, force_flag, dev) cmd = "%s %s '%s'" % (mkfs, force_flag, dev)
else: else:
cmd = "%s %s %s '%s'" % (mkfs, force_flag, opts, dev) cmd = "%s %s %s '%s'" % (mkfs, force_flag, opts, dev)
rc,_,err = module.run_command(cmd) rc, _, err = module.run_command(cmd)
if rc == 0: if rc == 0:
changed = True changed = True
else: else:
module.fail_json(msg="Creating filesystem %s on device '%s' failed"%(fstype,dev), rc=rc, err=err) module.fail_json(msg="Creating filesystem %s on device '%s' failed" % (fstype, dev), rc=rc, err=err)
module.exit_json(changed=changed) module.exit_json(changed=changed)

@ -1,23 +1,21 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2016, Kenneth D. Evensen <kevensen@redhat.com>
# (c) 2017, Abhijeet Kasurde <akasurde@redhat.com> # Copyright: (c) 2016, Kenneth D. Evensen <kevensen@redhat.com>
# # Copyright: (c) 2017, Abhijeet Kasurde <akasurde@redhat.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = """ DOCUMENTATION = """
module: gconftool2 module: gconftool2
author: author:
- "Kenneth D. Evensen (@kevensen)" - Kenneth D. Evensen (@kevensen)
short_description: Edit GNOME Configurations short_description: Edit GNOME Configurations
description: description:
- This module allows for the manipulation of GNOME 2 Configuration via - This module allows for the manipulation of GNOME 2 Configuration via
@ -25,47 +23,35 @@ description:
version_added: "2.3" version_added: "2.3"
options: options:
key: key:
required: true
description: description:
- A GConf preference key is an element in the GConf repository - A GConf preference key is an element in the GConf repository
that corresponds to an application preference. See man gconftool-2(1) that corresponds to an application preference. See man gconftool-2(1)
required: yes
value: value:
required: false
description: description:
- Preference keys typically have simple values such as strings, - Preference keys typically have simple values such as strings,
integers, or lists of strings and integers. This is ignored if the state integers, or lists of strings and integers. This is ignored if the state
is "get". See man gconftool-2(1) is "get". See man gconftool-2(1)
value_type: value_type:
required: false
choices:
- int
- bool
- float
- string
description: description:
- The type of value being set. This is ignored if the state is "get". - The type of value being set. This is ignored if the state is "get".
choices: [ bool, float, int, string ]
state: state:
required: true
choices:
- get
- present
- absent
description: description:
- The action to take upon the key/value. - The action to take upon the key/value.
required: yes
choices: [ absent, get, present ]
config_source: config_source:
required: false
description: description:
- Specify a configuration source to use rather than the default path. - Specify a configuration source to use rather than the default path.
See man gconftool-2(1) See man gconftool-2(1)
direct: direct:
required: false
choices: [ "yes", "no" ]
default: no
description: description:
- Access the config database directly, bypassing server. If direct is - Access the config database directly, bypassing server. If direct is
specified then the config_source must be specified as well. specified then the config_source must be specified as well.
See man gconftool-2(1) See man gconftool-2(1)
type: bool
default: 'no'
""" """
EXAMPLES = """ EXAMPLES = """
@ -81,12 +67,12 @@ RETURN = '''
description: The key specified in the module parameters description: The key specified in the module parameters
returned: success returned: success
type: string type: string
sample: "/desktop/gnome/interface/font_name" sample: /desktop/gnome/interface/font_name
value_type: value_type:
description: The type of the value that was changed description: The type of the value that was changed
returned: success returned: success
type: string type: string
sample: "string" sample: string
value: value:
description: The value of the preference key after executing the module description: The value of the preference key after executing the module
returned: success returned: success
@ -165,18 +151,13 @@ def main():
# Setup the Ansible module # Setup the Ansible module
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
key=dict(required=True, default=None, type='str'), key=dict(type='str', required=True),
value_type=dict(required=False, value_type=dict(type='str', choices=['bool', 'float', 'int', 'string']),
choices=['int', 'bool', 'float', 'string'], value=dict(type='str'),
type='str'), state=dict(type='str', required=True, choices=['absent', 'get', 'present']),
value=dict(required=False, default=None, type='str'), direct=dict(type='bool', default=False),
state=dict(required=True, config_source=dict(type='str'),
default=None, ),
choices=['present', 'get', 'absent'],
type='str'),
direct=dict(required=False, default=False, type='bool'),
config_source=dict(required=False, default=None, type='str')
),
supports_check_mode=True supports_check_mode=True
) )
@ -203,17 +184,17 @@ def main():
if state != "get": if state != "get":
if value is None or value == "": if value is None or value == "":
module.fail_json(msg='State %s requires "value" to be set' module.fail_json(msg='State %s requires "value" to be set'
% str(state)) % str(state))
elif value_type is None or value_type == "": elif value_type is None or value_type == "":
module.fail_json(msg='State %s requires "value_type" to be set' module.fail_json(msg='State %s requires "value_type" to be set'
% str(state)) % str(state))
if direct and config_source is None: if direct and config_source is None:
module.fail_json(msg='If "direct" is "yes" then the ' + module.fail_json(msg='If "direct" is "yes" then the ' +
'"config_source" must be specified') '"config_source" must be specified')
elif not direct and config_source is not None: elif not direct and config_source is not None:
module.fail_json(msg='If the "config_source" is specified ' + module.fail_json(msg='If the "config_source" is specified ' +
'then "direct" must be "yes"') 'then "direct" must be "yes"')
# Create a gconf2 preference # Create a gconf2 preference
gconf_pref = GConf2Preference(module, key, value_type, gconf_pref = GConf2Preference(module, key, value_type,
@ -247,5 +228,6 @@ def main():
module.exit_json(changed=change, ansible_facts=facts) module.exit_json(changed=change, ansible_facts=facts)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

@ -1,18 +1,16 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2014, Taneli Leppä <taneli@crasman.fi> # Copyright: (c) 2014, Taneli Leppä <taneli@crasman.fi>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = """ DOCUMENTATION = """
module: gluster_volume module: gluster_volume
short_description: Manage GlusterFS volumes short_description: Manage GlusterFS volumes
@ -22,75 +20,76 @@ version_added: '1.9'
options: options:
name: name:
description: description:
- The volume name - The volume name.
required: true required: true
state: state:
description: description:
- Use present/absent ensure if a volume exists or not. - Use present/absent ensure if a volume exists or not.
Use started/stopped to control its availability. Use started/stopped to control its availability.
required: true required: true
choices: ['present', 'absent', 'started', 'stopped'] choices: ['absent', 'present', 'started', 'stopped']
cluster: cluster:
description: description:
- List of hosts to use for probing and brick setup - List of hosts to use for probing and brick setup.
host: host:
description: description:
- Override local hostname (for peer probing purposes) - Override local hostname (for peer probing purposes).
replicas: replicas:
description: description:
- Replica count for volume - Replica count for volume.
arbiter: arbiter:
description: description:
- Arbiter count for volume - Arbiter count for volume.
version_added: '2.3' version_added: '2.3'
stripes: stripes:
description: description:
- Stripe count for volume - Stripe count for volume.
disperses: disperses:
description: description:
- Disperse count for volume - Disperse count for volume.
version_added: '2.2' version_added: '2.2'
redundancies: redundancies:
description: description:
- Redundancy count for volume - Redundancy count for volume.
version_added: '2.2' version_added: '2.2'
transport: transport:
description: description:
- Transport type for volume - Transport type for volume.
default: 'tcp' default: tcp
choices: ['tcp', 'rdma', 'tcp,rdma'] choices: [ rdma, tcp, tcp,rdma ]
bricks: bricks:
description: description:
- Brick paths on servers. Multiple brick paths can be separated by commas. - Brick paths on servers. Multiple brick paths can be separated by commas.
aliases: ['brick'] aliases: [ brick ]
start_on_create: start_on_create:
description: description:
- Controls whether the volume is started after creation or not - Controls whether the volume is started after creation or not.
default: 'yes'
type: bool type: bool
default: 'yes'
rebalance: rebalance:
description: description:
- Controls whether the cluster is rebalanced after changes - Controls whether the cluster is rebalanced after changes.
default: 'no'
type: bool type: bool
default: 'no'
directory: directory:
description: description:
- Directory for limit-usage - Directory for limit-usage.
options: options:
description: description:
- A dictionary/hash with options/settings for the volume - A dictionary/hash with options/settings for the volume.
quota: quota:
description: description:
- Quota value for limit-usage (be sure to use 10.0MB instead of 10MB, see quota list) - Quota value for limit-usage (be sure to use 10.0MB instead of 10MB, see quota list).
force: force:
description: description:
- If brick is being created in the root partition, module will fail. - If brick is being created in the root partition, module will fail.
Set force to true to override this behaviour. Set force to true to override this behaviour.
type: bool type: bool
notes: notes:
- Requires cli tools for GlusterFS on servers - Requires cli tools for GlusterFS on servers.
- Will add new bricks, but not remove them - Will add new bricks, but not remove them.
author: Taneli Leppä (@rosmo) author:
- Taneli Leppä (@rosmo)
""" """
EXAMPLES = """ EXAMPLES = """
@ -153,7 +152,6 @@ import traceback
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
glusterbin = '' glusterbin = ''
@ -166,12 +164,13 @@ def run_gluster(gargs, **kwargs):
rc, out, err = module.run_command(args, **kwargs) rc, out, err = module.run_command(args, **kwargs)
if rc != 0: if rc != 0:
module.fail_json(msg='error running gluster (%s) command (rc=%d): %s' % module.fail_json(msg='error running gluster (%s) command (rc=%d): %s' %
(' '.join(args), rc, out or err), exception=traceback.format_exc()) (' '.join(args), rc, out or err), exception=traceback.format_exc())
except Exception as e: except Exception as e:
module.fail_json(msg='error running gluster (%s) command: %s' % (' '.join(args), module.fail_json(msg='error running gluster (%s) command: %s' % (' '.join(args),
to_native(e)), exception=traceback.format_exc()) to_native(e)), exception=traceback.format_exc())
return out return out
def run_gluster_nofail(gargs, **kwargs): def run_gluster_nofail(gargs, **kwargs):
global glusterbin global glusterbin
global module global module
@ -182,8 +181,9 @@ def run_gluster_nofail(gargs, **kwargs):
return None return None
return out return out
def get_peers(): def get_peers():
out = run_gluster([ 'peer', 'status']) out = run_gluster(['peer', 'status'])
peers = {} peers = {}
hostname = None hostname = None
uuid = None uuid = None
@ -208,8 +208,9 @@ def get_peers():
shortNames = False shortNames = False
return peers return peers
def get_volumes(): def get_volumes():
out = run_gluster([ 'volume', 'info' ]) out = run_gluster(['volume', 'info'])
volumes = {} volumes = {}
volume = {} volume = {}
@ -227,17 +228,17 @@ def get_volumes():
if key.lower() == 'transport-type': if key.lower() == 'transport-type':
volume['transport'] = value volume['transport'] = value
if value.lower().endswith(' (arbiter)'): if value.lower().endswith(' (arbiter)'):
if not 'arbiters' in volume: if 'arbiters' not in volume:
volume['arbiters'] = [] volume['arbiters'] = []
value = value[:-10] value = value[:-10]
volume['arbiters'].append(value) volume['arbiters'].append(value)
if key.lower() != 'bricks' and key.lower()[:5] == 'brick': if key.lower() != 'bricks' and key.lower()[:5] == 'brick':
if not 'bricks' in volume: if 'bricks' not in volume:
volume['bricks'] = [] volume['bricks'] = []
volume['bricks'].append(value) volume['bricks'].append(value)
# Volume options # Volume options
if '.' in key: if '.' in key:
if not 'options' in volume: if 'options' not in volume:
volume['options'] = {} volume['options'] = {}
volume['options'][key] = value volume['options'][key] = value
if key == 'features.quota' and value == 'on': if key == 'features.quota' and value == 'on':
@ -249,20 +250,22 @@ def get_volumes():
volume = {} volume = {}
return volumes return volumes
def get_quotas(name, nofail): def get_quotas(name, nofail):
quotas = {} quotas = {}
if nofail: if nofail:
out = run_gluster_nofail([ 'volume', 'quota', name, 'list' ]) out = run_gluster_nofail(['volume', 'quota', name, 'list'])
if not out: if not out:
return quotas return quotas
else: else:
out = run_gluster([ 'volume', 'quota', name, 'list' ]) out = run_gluster(['volume', 'quota', name, 'list'])
for row in out.split('\n'): for row in out.split('\n'):
if row[:1] == '/': if row[:1] == '/':
q = re.split('\s+', row) q = re.split('\s+', row)
quotas[q[0]] = q[1] quotas[q[0]] = q[1]
return quotas return quotas
def wait_for_peer(host): def wait_for_peer(host):
for x in range(0, 4): for x in range(0, 4):
peers = get_peers() peers = get_peers()
@ -271,20 +274,23 @@ def wait_for_peer(host):
time.sleep(1) time.sleep(1)
return False return False
def probe(host, myhostname): def probe(host, myhostname):
global module global module
out = run_gluster([ 'peer', 'probe', host ]) out = run_gluster(['peer', 'probe', host])
if out.find('localhost') == -1 and not wait_for_peer(host): if out.find('localhost') == -1 and not wait_for_peer(host):
module.fail_json(msg='failed to probe peer %s on %s' % (host, myhostname)) module.fail_json(msg='failed to probe peer %s on %s' % (host, myhostname))
def probe_all_peers(hosts, peers, myhostname): def probe_all_peers(hosts, peers, myhostname):
for host in hosts: for host in hosts:
host = host.strip() # Clean up any extra space for exact comparison host = host.strip() # Clean up any extra space for exact comparison
if host not in peers: if host not in peers:
probe(host, myhostname) probe(host, myhostname)
def create_volume(name, stripe, replica, arbiter, disperse, redundancy, transport, hosts, bricks, force): def create_volume(name, stripe, replica, arbiter, disperse, redundancy, transport, hosts, bricks, force):
args = [ 'volume', 'create' ] args = ['volume', 'create']
args.append(name) args.append(name)
if stripe: if stripe:
args.append('stripe') args.append('stripe')
@ -310,17 +316,21 @@ def create_volume(name, stripe, replica, arbiter, disperse, redundancy, transpor
args.append('force') args.append('force')
run_gluster(args) run_gluster(args)
def start_volume(name): def start_volume(name):
run_gluster([ 'volume', 'start', name ]) run_gluster(['volume', 'start', name])
def stop_volume(name): def stop_volume(name):
run_gluster([ 'volume', 'stop', name ]) run_gluster(['volume', 'stop', name])
def set_volume_option(name, option, parameter): def set_volume_option(name, option, parameter):
run_gluster([ 'volume', 'set', name, option, parameter ]) run_gluster(['volume', 'set', name, option, parameter])
def add_bricks(name, new_bricks, stripe, replica, force): def add_bricks(name, new_bricks, stripe, replica, force):
args = [ 'volume', 'add-brick', name ] args = ['volume', 'add-brick', name]
if stripe: if stripe:
args.append('stripe') args.append('stripe')
args.append(str(stripe)) args.append(str(stripe))
@ -332,41 +342,44 @@ def add_bricks(name, new_bricks, stripe, replica, force):
args.append('force') args.append('force')
run_gluster(args) run_gluster(args)
def do_rebalance(name): def do_rebalance(name):
run_gluster([ 'volume', 'rebalance', name, 'start' ]) run_gluster(['volume', 'rebalance', name, 'start'])
def enable_quota(name): def enable_quota(name):
run_gluster([ 'volume', 'quota', name, 'enable' ]) run_gluster(['volume', 'quota', name, 'enable'])
def set_quota(name, directory, value): def set_quota(name, directory, value):
run_gluster([ 'volume', 'quota', name, 'limit-usage', directory, value ]) run_gluster(['volume', 'quota', name, 'limit-usage', directory, value])
def main(): def main():
### MAIN ### # MAIN
global module global module
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
name=dict(required=True, aliases=['volume']), name=dict(type='str', required=True, aliases=['volume']),
state=dict(required=True, choices=['present', 'absent', 'started', 'stopped']), state=dict(type='str', required=True, choices=['absent', 'started', 'stopped', 'present']),
cluster=dict(default=None, type='list'), cluster=dict(type='list'),
host=dict(default=None), host=dict(type='str'),
stripes=dict(default=None, type='int'), stripes=dict(type='int'),
replicas=dict(default=None, type='int'), replicas=dict(type='int'),
arbiters=dict(default=None, type='int'), arbiters=dict(type='int'),
disperses=dict(default=None, type='int'), disperses=dict(type='int'),
redundancies=dict(default=None, type='int'), redundancies=dict(type='int'),
transport=dict(default='tcp', choices=['tcp', 'rdma', 'tcp,rdma']), transport=dict(type='str', default='tcp', choices=['tcp', 'rdma', 'tcp,rdma']),
bricks=dict(default=None, aliases=['brick']), bricks=dict(type='str', aliases=['brick']),
start_on_create=dict(default=True, type='bool'), start_on_create=dict(type='bool', default=True),
rebalance=dict(default=False, type='bool'), rebalance=dict(type='bool', default=False),
options=dict(default={}, type='dict'), options=dict(type='dict', default={}),
quota=dict(), quota=dict(type='str'),
directory=dict(default=None), directory=dict(type='str'),
force=dict(default=False, type='bool'), force=dict(type='bool', default=False),
) ),
) )
global glusterbin global glusterbin
glusterbin = module.get_bin_path('gluster', True) glusterbin = module.get_bin_path('gluster', True)
@ -375,7 +388,7 @@ def main():
action = module.params['state'] action = module.params['state']
volume_name = module.params['name'] volume_name = module.params['name']
cluster= module.params['cluster'] cluster = module.params['cluster']
brick_paths = module.params['bricks'] brick_paths = module.params['bricks']
stripes = module.params['stripes'] stripes = module.params['stripes']
replicas = module.params['replicas'] replicas = module.params['replicas']
@ -408,7 +421,6 @@ def main():
quota = module.params['quota'] quota = module.params['quota']
directory = module.params['directory'] directory = module.params['directory']
# get current state info # get current state info
peers = get_peers() peers = get_peers()
volumes = get_volumes() volumes = get_volumes()
@ -421,7 +433,7 @@ def main():
if volume_name in volumes: if volume_name in volumes:
if volumes[volume_name]['status'].lower() != 'stopped': if volumes[volume_name]['status'].lower() != 'stopped':
stop_volume(volume_name) stop_volume(volume_name)
run_gluster([ 'volume', 'delete', volume_name ]) run_gluster(['volume', 'delete', volume_name])
changed = True changed = True
if action == 'present': if action == 'present':
@ -495,7 +507,7 @@ def main():
do_rebalance(volume_name) do_rebalance(volume_name)
facts = {} facts = {}
facts['glusterfs'] = { 'peers': peers, 'volumes': volumes, 'quotas': quotas } facts['glusterfs'] = {'peers': peers, 'volumes': volumes, 'quotas': quotas}
module.exit_json(changed=changed, ansible_facts=facts) module.exit_json(changed=changed, ansible_facts=facts)

@ -1,56 +1,55 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2012, Stephen Fromm <sfromm@gmail.com> # Copyright: (c) 2012, Stephen Fromm <sfromm@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'], 'status': ['stableinterface'],
'supported_by': 'core'} 'supported_by': 'core'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: group module: group
author: "Stephen Fromm (@sfromm)" author:
- Stephen Fromm (@sfromm)
version_added: "0.0.2" version_added: "0.0.2"
short_description: Add or remove groups short_description: Add or remove groups
requirements: [ groupadd, groupdel, groupmod ] requirements:
- groupadd
- groupdel
- groupmod
description: description:
- Manage presence of groups on a host. - Manage presence of groups on a host.
- For Windows targets, use the M(win_group) module instead. - For Windows targets, use the M(win_group) module instead.
options: options:
name: name:
required: true
description: description:
- Name of the group to manage. - Name of the group to manage.
required: true
gid: gid:
required: false
description: description:
- Optional I(GID) to set for the group. - Optional I(GID) to set for the group.
state: state:
required: false
default: "present"
choices: [ present, absent ]
description: description:
- Whether the group should be present or not on the remote host. - Whether the group should be present or not on the remote host.
choices: [ absent, present ]
default: present
system: system:
required: false
default: "no"
choices: [ "yes", "no" ]
description: description:
- If I(yes), indicates that the group created is a system group. - If I(yes), indicates that the group created is a system group.
type: bool
default: 'no'
notes: notes:
- For Windows targets, use the M(win_group) module instead. - For Windows targets, use the M(win_group) module instead.
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Example group command from Ansible Playbooks - name: Ensure group "somegroup" exists
- group: group:
name: somegroup name: somegroup
state: present state: present
''' '''
@ -81,11 +80,11 @@ class Group(object):
return load_platform_subclass(Group, args, kwargs) return load_platform_subclass(Group, args, kwargs)
def __init__(self, module): def __init__(self, module):
self.module = module self.module = module
self.state = module.params['state'] self.state = module.params['state']
self.name = module.params['name'] self.name = module.params['name']
self.gid = module.params['gid'] self.gid = module.params['gid']
self.system = module.params['system'] self.system = module.params['system']
def execute_command(self, cmd): def execute_command(self, cmd):
return self.module.run_command(cmd) return self.module.run_command(cmd)
@ -136,6 +135,7 @@ class Group(object):
return False return False
return info return info
# =========================================== # ===========================================
class SunOS(Group): class SunOS(Group):
@ -205,6 +205,7 @@ class AIX(Group):
cmd.append(self.name) cmd.append(self.name)
return self.execute_command(cmd) return self.execute_command(cmd)
# =========================================== # ===========================================
class FreeBsdGroup(Group): class FreeBsdGroup(Group):
@ -246,9 +247,8 @@ class FreeBsdGroup(Group):
return self.execute_command(cmd) return self.execute_command(cmd)
return (None, '', '') return (None, '', '')
# ===========================================
# ===========================================
class DarwinGroup(Group): class DarwinGroup(Group):
""" """
@ -267,22 +267,22 @@ class DarwinGroup(Group):
def group_add(self, **kwargs): def group_add(self, **kwargs):
cmd = [self.module.get_bin_path('dseditgroup', True)] cmd = [self.module.get_bin_path('dseditgroup', True)]
cmd += [ '-o', 'create' ] cmd += ['-o', 'create']
if self.gid is not None: if self.gid is not None:
cmd += [ '-i', self.gid ] cmd += ['-i', self.gid]
elif 'system' in kwargs and kwargs['system'] is True: elif 'system' in kwargs and kwargs['system'] is True:
gid = self.get_lowest_available_system_gid() gid = self.get_lowest_available_system_gid()
if gid is not False: if gid is not False:
self.gid = str(gid) self.gid = str(gid)
cmd += [ '-i', self.gid ] cmd += ['-i', self.gid]
cmd += [ '-L', self.name ] cmd += ['-L', self.name]
(rc, out, err) = self.execute_command(cmd) (rc, out, err) = self.execute_command(cmd)
return (rc, out, err) return (rc, out, err)
def group_del(self): def group_del(self):
cmd = [self.module.get_bin_path('dseditgroup', True)] cmd = [self.module.get_bin_path('dseditgroup', True)]
cmd += [ '-o', 'delete' ] cmd += ['-o', 'delete']
cmd += [ '-L', self.name ] cmd += ['-L', self.name]
(rc, out, err) = self.execute_command(cmd) (rc, out, err) = self.execute_command(cmd)
return (rc, out, err) return (rc, out, err)
@ -290,10 +290,10 @@ class DarwinGroup(Group):
info = self.group_info() info = self.group_info()
if self.gid is not None and int(self.gid) != info[2]: if self.gid is not None and int(self.gid) != info[2]:
cmd = [self.module.get_bin_path('dseditgroup', True)] cmd = [self.module.get_bin_path('dseditgroup', True)]
cmd += [ '-o', 'edit' ] cmd += ['-o', 'edit']
if gid is not None: if gid is not None:
cmd += [ '-i', gid ] cmd += ['-i', gid]
cmd += [ '-L', self.name ] cmd += ['-L', self.name]
(rc, out, err) = self.execute_command(cmd) (rc, out, err) = self.execute_command(cmd)
return (rc, out, err) return (rc, out, err)
return (None, '', '') return (None, '', '')
@ -302,7 +302,7 @@ class DarwinGroup(Group):
# check for lowest available system gid (< 500) # check for lowest available system gid (< 500)
try: try:
cmd = [self.module.get_bin_path('dscl', True)] cmd = [self.module.get_bin_path('dscl', True)]
cmd += [ '/Local/Default', '-list', '/Groups', 'PrimaryGroupID'] cmd += ['/Local/Default', '-list', '/Groups', 'PrimaryGroupID']
(rc, out, err) = self.execute_command(cmd) (rc, out, err) = self.execute_command(cmd)
lines = out.splitlines() lines = out.splitlines()
highest = 0 highest = 0
@ -318,6 +318,7 @@ class DarwinGroup(Group):
except: except:
return False return False
class OpenBsdGroup(Group): class OpenBsdGroup(Group):
""" """
This is a OpenBSD Group manipulation class. This is a OpenBSD Group manipulation class.
@ -357,6 +358,7 @@ class OpenBsdGroup(Group):
cmd.append(self.name) cmd.append(self.name)
return self.execute_command(cmd) return self.execute_command(cmd)
# =========================================== # ===========================================
class NetBsdGroup(Group): class NetBsdGroup(Group):
@ -398,17 +400,18 @@ class NetBsdGroup(Group):
cmd.append(self.name) cmd.append(self.name)
return self.execute_command(cmd) return self.execute_command(cmd)
# =========================================== # ===========================================
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec=dict(
state=dict(default='present', choices=['present', 'absent'], type='str'), state=dict(type='str', default='present', choices=['absent', 'present']),
name=dict(required=True, type='str'), name=dict(type='str', required=True),
gid=dict(default=None, type='str'), gid=dict(type='str'),
system=dict(default=False, type='bool'), system=dict(type='bool', default=False),
), ),
supports_check_mode=True supports_check_mode=True,
) )
group = Group(module) group = Group(module)

@ -200,7 +200,7 @@ class DebianStrategy(GenericStrategy):
open(self.HOSTNAME_FILE, "a").write("") open(self.HOSTNAME_FILE, "a").write("")
except IOError as e: except IOError as e:
self.module.fail_json(msg="failed to write file: %s" % self.module.fail_json(msg="failed to write file: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
try: try:
f = open(self.HOSTNAME_FILE) f = open(self.HOSTNAME_FILE)
try: try:
@ -209,7 +209,7 @@ class DebianStrategy(GenericStrategy):
f.close() f.close()
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to read hostname: %s" % self.module.fail_json(msg="failed to read hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
def set_permanent_hostname(self, name): def set_permanent_hostname(self, name):
try: try:
@ -220,7 +220,7 @@ class DebianStrategy(GenericStrategy):
f.close() f.close()
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to update hostname: %s" % self.module.fail_json(msg="failed to update hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
class SLESStrategy(GenericStrategy): class SLESStrategy(GenericStrategy):
@ -236,7 +236,7 @@ class SLESStrategy(GenericStrategy):
open(self.HOSTNAME_FILE, "a").write("") open(self.HOSTNAME_FILE, "a").write("")
except IOError as e: except IOError as e:
self.module.fail_json(msg="failed to write file: %s" % self.module.fail_json(msg="failed to write file: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
try: try:
f = open(self.HOSTNAME_FILE) f = open(self.HOSTNAME_FILE)
try: try:
@ -245,7 +245,7 @@ class SLESStrategy(GenericStrategy):
f.close() f.close()
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to read hostname: %s" % self.module.fail_json(msg="failed to read hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
def set_permanent_hostname(self, name): def set_permanent_hostname(self, name):
try: try:
@ -256,7 +256,7 @@ class SLESStrategy(GenericStrategy):
f.close() f.close()
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to update hostname: %s" % self.module.fail_json(msg="failed to update hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
class RedHatStrategy(GenericStrategy): class RedHatStrategy(GenericStrategy):
@ -278,7 +278,7 @@ class RedHatStrategy(GenericStrategy):
f.close() f.close()
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to read hostname: %s" % self.module.fail_json(msg="failed to read hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
def set_permanent_hostname(self, name): def set_permanent_hostname(self, name):
try: try:
@ -303,7 +303,7 @@ class RedHatStrategy(GenericStrategy):
f.close() f.close()
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to update hostname: %s" % self.module.fail_json(msg="failed to update hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
class AlpineStrategy(GenericStrategy): class AlpineStrategy(GenericStrategy):
@ -325,7 +325,7 @@ class AlpineStrategy(GenericStrategy):
open(self.HOSTNAME_FILE, "a").write("") open(self.HOSTNAME_FILE, "a").write("")
except IOError as e: except IOError as e:
self.module.fail_json(msg="failed to write file: %s" % self.module.fail_json(msg="failed to write file: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
try: try:
f = open(self.HOSTNAME_FILE) f = open(self.HOSTNAME_FILE)
try: try:
@ -334,7 +334,7 @@ class AlpineStrategy(GenericStrategy):
f.close() f.close()
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to read hostname: %s" % self.module.fail_json(msg="failed to read hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
def set_permanent_hostname(self, name): def set_permanent_hostname(self, name):
try: try:
@ -345,7 +345,7 @@ class AlpineStrategy(GenericStrategy):
f.close() f.close()
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to update hostname: %s" % self.module.fail_json(msg="failed to update hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
def set_current_hostname(self, name): def set_current_hostname(self, name):
cmd = [self.hostname_cmd, '-F', self.HOSTNAME_FILE] cmd = [self.hostname_cmd, '-F', self.HOSTNAME_FILE]
@ -413,7 +413,7 @@ class OpenRCStrategy(GenericStrategy):
return line[10:].strip('"') return line[10:].strip('"')
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to read hostname: %s" % self.module.fail_json(msg="failed to read hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
finally: finally:
f.close() f.close()
@ -435,7 +435,7 @@ class OpenRCStrategy(GenericStrategy):
f.write('\n'.join(lines) + '\n') f.write('\n'.join(lines) + '\n')
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to update hostname: %s" % self.module.fail_json(msg="failed to update hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
finally: finally:
f.close() f.close()
@ -454,7 +454,7 @@ class OpenBSDStrategy(GenericStrategy):
open(self.HOSTNAME_FILE, "a").write("") open(self.HOSTNAME_FILE, "a").write("")
except IOError as e: except IOError as e:
self.module.fail_json(msg="failed to write file: %s" % self.module.fail_json(msg="failed to write file: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
try: try:
f = open(self.HOSTNAME_FILE) f = open(self.HOSTNAME_FILE)
try: try:
@ -463,7 +463,7 @@ class OpenBSDStrategy(GenericStrategy):
f.close() f.close()
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to read hostname: %s" % self.module.fail_json(msg="failed to read hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
def set_permanent_hostname(self, name): def set_permanent_hostname(self, name):
try: try:
@ -474,7 +474,7 @@ class OpenBSDStrategy(GenericStrategy):
f.close() f.close()
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to update hostname: %s" % self.module.fail_json(msg="failed to update hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
class SolarisStrategy(GenericStrategy): class SolarisStrategy(GenericStrategy):
@ -521,7 +521,7 @@ class FreeBSDStrategy(GenericStrategy):
open(self.HOSTNAME_FILE, "a").write("hostname=temporarystub\n") open(self.HOSTNAME_FILE, "a").write("hostname=temporarystub\n")
except IOError as e: except IOError as e:
self.module.fail_json(msg="failed to write file: %s" % self.module.fail_json(msg="failed to write file: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
try: try:
try: try:
f = open(self.HOSTNAME_FILE, 'r') f = open(self.HOSTNAME_FILE, 'r')
@ -531,7 +531,7 @@ class FreeBSDStrategy(GenericStrategy):
return line[10:].strip('"') return line[10:].strip('"')
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to read hostname: %s" % self.module.fail_json(msg="failed to read hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
finally: finally:
f.close() f.close()
@ -553,7 +553,7 @@ class FreeBSDStrategy(GenericStrategy):
f.write('\n'.join(lines) + '\n') f.write('\n'.join(lines) + '\n')
except Exception as e: except Exception as e:
self.module.fail_json(msg="failed to update hostname: %s" % self.module.fail_json(msg="failed to update hostname: %s" %
to_native(e), exception=traceback.format_exc()) to_native(e), exception=traceback.format_exc())
finally: finally:
f.close() f.close()

@ -1,17 +1,15 @@
#!/usr/bin/python #!/usr/bin/python
# #
# (c) 2013, RSD Services S.A # Copyright: (c) 2013, RSD Services S.A
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: java_cert module: java_cert
@ -65,10 +63,10 @@ options:
state: state:
description: description:
- Defines action which can be either certificate import or removal. - Defines action which can be either certificate import or removal.
choices: [ 'present', 'absent' ] choices: [ absent, present ]
default: present default: present
author:
author: Adam Hamsik @haad - Adam Hamsik (@haad)
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -131,17 +129,19 @@ import os
# import module snippets # import module snippets
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
def check_cert_present(module, executable, keystore_path, keystore_pass, alias): def check_cert_present(module, executable, keystore_path, keystore_pass, alias):
''' Check if certificate with alias is present in keystore ''' Check if certificate with alias is present in keystore
located at keystore_path ''' located at keystore_path '''
test_cmd = ("%s -noprompt -list -keystore '%s' -storepass '%s' " test_cmd = ("%s -noprompt -list -keystore '%s' -storepass '%s' "
"-alias '%s'")%(executable, keystore_path, keystore_pass, alias) "-alias '%s'") % (executable, keystore_path, keystore_pass, alias)
(check_rc, _, _) = module.run_command(test_cmd) (check_rc, _, _) = module.run_command(test_cmd)
if check_rc == 0: if check_rc == 0:
return True return True
return False return False
def import_cert_url(module, executable, url, port, keystore_path, keystore_pass, alias): def import_cert_url(module, executable, url, port, keystore_path, keystore_pass, alias):
''' Import certificate from URL into keystore located at keystore_path ''' ''' Import certificate from URL into keystore located at keystore_path '''
import re import re
@ -152,7 +152,7 @@ def import_cert_url(module, executable, url, port, keystore_path, keystore_pass,
proxy_opts = '' proxy_opts = ''
if https_proxy is not None: if https_proxy is not None:
(proxy_host, proxy_port) = https_proxy.split(':') (proxy_host, proxy_port) = https_proxy.split(':')
proxy_opts = ("-J-Dhttps.proxyHost=%s -J-Dhttps.proxyPort=%s")%(proxy_host, proxy_port) proxy_opts = ("-J-Dhttps.proxyHost=%s -J-Dhttps.proxyPort=%s") % (proxy_host, proxy_port)
if no_proxy is not None: if no_proxy is not None:
# For Java's nonProxyHosts property, items are separated by '|', # For Java's nonProxyHosts property, items are separated by '|',
@ -162,13 +162,13 @@ def import_cert_url(module, executable, url, port, keystore_path, keystore_pass,
# The property name is http.nonProxyHosts, there is no # The property name is http.nonProxyHosts, there is no
# separate setting for HTTPS. # separate setting for HTTPS.
proxy_opts += (" -J-Dhttp.nonProxyHosts='%s'")%(non_proxy_hosts) proxy_opts += (" -J-Dhttp.nonProxyHosts='%s'") % (non_proxy_hosts)
fetch_cmd = ("%s -printcert -rfc -sslserver %s %s:%d")%(executable, proxy_opts, url, port) fetch_cmd = ("%s -printcert -rfc -sslserver %s %s:%d") % (executable, proxy_opts, url, port)
import_cmd = ("%s -importcert -noprompt -keystore '%s' " import_cmd = ("%s -importcert -noprompt -keystore '%s' "
"-storepass '%s' -alias '%s'")%(executable, keystore_path, "-storepass '%s' -alias '%s'") % (executable, keystore_path,
keystore_pass, alias) keystore_pass, alias)
if module.check_mode: if module.check_mode:
module.exit_json(changed=True) module.exit_json(changed=True)
@ -180,7 +180,7 @@ def import_cert_url(module, executable, url, port, keystore_path, keystore_pass,
(import_rc, import_out, import_err) = module.run_command(import_cmd, (import_rc, import_out, import_err) = module.run_command(import_cmd,
data=fetch_out, data=fetch_out,
check_rc=False) check_rc=False)
diff = {'before': '\n', 'after': '%s\n'%alias} diff = {'before': '\n', 'after': '%s\n' % alias}
if import_rc == 0: if import_rc == 0:
return module.exit_json(changed=True, msg=import_out, return module.exit_json(changed=True, msg=import_out,
rc=import_rc, cmd=import_cmd, stdout=import_out, rc=import_rc, cmd=import_cmd, stdout=import_out,
@ -189,14 +189,15 @@ def import_cert_url(module, executable, url, port, keystore_path, keystore_pass,
return module.fail_json(msg=import_out, rc=import_rc, cmd=import_cmd, return module.fail_json(msg=import_out, rc=import_rc, cmd=import_cmd,
error=import_err) error=import_err)
def import_cert_path(module, executable, path, keystore_path, keystore_pass, alias): def import_cert_path(module, executable, path, keystore_path, keystore_pass, alias):
''' Import certificate from path into keystore located on ''' Import certificate from path into keystore located on
keystore_path as alias ''' keystore_path as alias '''
import_cmd = ("%s -importcert -noprompt -keystore '%s' " import_cmd = ("%s -importcert -noprompt -keystore '%s' "
"-storepass '%s' -file '%s' -alias '%s'")%(executable, "-storepass '%s' -file '%s' -alias '%s'") % (executable,
keystore_path, keystore_path,
keystore_pass, keystore_pass,
path, alias) path, alias)
if module.check_mode: if module.check_mode:
module.exit_json(changed=True) module.exit_json(changed=True)
@ -205,7 +206,7 @@ def import_cert_path(module, executable, path, keystore_path, keystore_pass, ali
(import_rc, import_out, import_err) = module.run_command(import_cmd, (import_rc, import_out, import_err) = module.run_command(import_cmd,
check_rc=False) check_rc=False)
diff = {'before': '\n', 'after': '%s\n'%alias} diff = {'before': '\n', 'after': '%s\n' % alias}
if import_rc == 0: if import_rc == 0:
return module.exit_json(changed=True, msg=import_out, return module.exit_json(changed=True, msg=import_out,
rc=import_rc, cmd=import_cmd, stdout=import_out, rc=import_rc, cmd=import_cmd, stdout=import_out,
@ -213,6 +214,7 @@ def import_cert_path(module, executable, path, keystore_path, keystore_pass, ali
else: else:
return module.fail_json(msg=import_out, rc=import_rc, cmd=import_cmd) return module.fail_json(msg=import_out, rc=import_rc, cmd=import_cmd)
def import_pkcs12_path(module, executable, path, keystore_path, keystore_pass, pkcs12_pass, pkcs12_alias, alias): def import_pkcs12_path(module, executable, path, keystore_path, keystore_pass, pkcs12_pass, pkcs12_alias, alias):
''' Import pkcs12 from path into keystore located on ''' Import pkcs12 from path into keystore located on
keystore_path as alias ''' keystore_path as alias '''
@ -228,7 +230,7 @@ def import_pkcs12_path(module, executable, path, keystore_path, keystore_pass, p
(import_rc, import_out, import_err) = module.run_command(import_cmd, (import_rc, import_out, import_err) = module.run_command(import_cmd,
check_rc=False) check_rc=False)
diff = {'before': '\n', 'after': '%s\n'%alias} diff = {'before': '\n', 'after': '%s\n' % alias}
if import_rc == 0: if import_rc == 0:
return module.exit_json(changed=True, msg=import_out, return module.exit_json(changed=True, msg=import_out,
rc=import_rc, cmd=import_cmd, stdout=import_out, rc=import_rc, cmd=import_cmd, stdout=import_out,
@ -240,7 +242,7 @@ def import_pkcs12_path(module, executable, path, keystore_path, keystore_pass, p
def delete_cert(module, executable, keystore_path, keystore_pass, alias): def delete_cert(module, executable, keystore_path, keystore_pass, alias):
''' Delete certificate identified with alias from keystore on keystore_path ''' ''' Delete certificate identified with alias from keystore on keystore_path '''
del_cmd = ("%s -delete -keystore '%s' -storepass '%s' " del_cmd = ("%s -delete -keystore '%s' -storepass '%s' "
"-alias '%s'")%(executable, keystore_path, keystore_pass, alias) "-alias '%s'") % (executable, keystore_path, keystore_pass, alias)
if module.check_mode: if module.check_mode:
module.exit_json(changed=True) module.exit_json(changed=True)
@ -248,28 +250,31 @@ def delete_cert(module, executable, keystore_path, keystore_pass, alias):
# Delete SSL certificate from keystore # Delete SSL certificate from keystore
(del_rc, del_out, del_err) = module.run_command(del_cmd, check_rc=True) (del_rc, del_out, del_err) = module.run_command(del_cmd, check_rc=True)
diff = {'before': '%s\n'%alias, 'after': None} diff = {'before': '%s\n' % alias, 'after': None}
return module.exit_json(changed=True, msg=del_out, return module.exit_json(changed=True, msg=del_out,
rc=del_rc, cmd=del_cmd, stdout=del_out, rc=del_rc, cmd=del_cmd, stdout=del_out,
error=del_err, diff=diff) error=del_err, diff=diff)
def test_keytool(module, executable): def test_keytool(module, executable):
''' Test if keytool is actuall executable or not ''' ''' Test if keytool is actuall executable or not '''
test_cmd = "%s"%(executable) test_cmd = "%s" % (executable)
module.run_command(test_cmd, check_rc=True) module.run_command(test_cmd, check_rc=True)
def test_keystore(module, keystore_path): def test_keystore(module, keystore_path):
''' Check if we can access keystore as file or not ''' ''' Check if we can access keystore as file or not '''
if keystore_path is None: if keystore_path is None:
keystore_path = '' keystore_path = ''
if not os.path.exists(keystore_path) and not os.path.isfile(keystore_path): if not os.path.exists(keystore_path) and not os.path.isfile(keystore_path):
## Keystore doesn't exist we want to create it # Keystore doesn't exist we want to create it
return module.fail_json(changed=False, return module.fail_json(changed=False,
msg="Module require existing keystore at keystore_path '%s'" msg="Module require existing keystore at keystore_path '%s'"
% (keystore_path)) % (keystore_path))
def main(): def main():
argument_spec = dict( argument_spec = dict(
@ -279,13 +284,12 @@ def main():
pkcs12_password=dict(type='str', no_log=True), pkcs12_password=dict(type='str', no_log=True),
pkcs12_alias=dict(type='str'), pkcs12_alias=dict(type='str'),
cert_alias=dict(type='str'), cert_alias=dict(type='str'),
cert_port=dict(default='443', type='int'), cert_port=dict(type='int', default='443'),
keystore_path=dict(type='path'), keystore_path=dict(type='path'),
keystore_pass=dict(required=True, type='str', no_log=True), keystore_pass=dict(type='str', required=True, no_log=True),
keystore_create=dict(default=False, type='bool'), keystore_create=dict(type='bool', default=False),
executable=dict(default='keytool', type='str'), executable=dict(type='str', default='keytool'),
state=dict(default='present', state=dict(type='str', default='present', choices=['absent', 'present']),
choices=['present', 'absent'])
) )
module = AnsibleModule( module = AnsibleModule(
@ -317,7 +321,7 @@ def main():
if path and not cert_alias: if path and not cert_alias:
module.fail_json(changed=False, module.fail_json(changed=False,
msg="Using local path import from %s requires alias argument." msg="Using local path import from %s requires alias argument."
%(keystore_path)) % (keystore_path))
test_keytool(module, executable) test_keytool(module, executable)
@ -347,5 +351,6 @@ def main():
module.exit_json(changed=False) module.exit_json(changed=False)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

@ -1,49 +1,44 @@
#!/usr/bin/python #!/usr/bin/python
# encoding: utf-8 -*- # encoding: utf-8 -*-
# (c) 2013, Matthias Vogelgesang <matthias.vogelgesang@gmail.com> # Copyright: (c) 2013, Matthias Vogelgesang <matthias.vogelgesang@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: kernel_blacklist module: kernel_blacklist
author: "Matthias Vogelgesang (@matze)" author:
version_added: 1.4 - Matthias Vogelgesang (@matze)
version_added: '1.4'
short_description: Blacklist kernel modules short_description: Blacklist kernel modules
description: description:
- Add or remove kernel modules from blacklist. - Add or remove kernel modules from blacklist.
options: options:
name: name:
required: true
description: description:
- Name of kernel module to black- or whitelist. - Name of kernel module to black- or whitelist.
required: true
state: state:
required: false
default: "present"
choices: [ present, absent ]
description: description:
- Whether the module should be present in the blacklist or absent. - Whether the module should be present in the blacklist or absent.
choices: [ absent, present ]
default: present
blacklist_file: blacklist_file:
required: false
description: description:
- If specified, use this blacklist file instead of - If specified, use this blacklist file instead of
C(/etc/modprobe.d/blacklist-ansible.conf). C(/etc/modprobe.d/blacklist-ansible.conf).
default: null
requirements: []
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Blacklist the nouveau driver module - name: Blacklist the nouveau driver module
- kernel_blacklist: kernel_blacklist:
name: nouveau name: nouveau
state: present state: present
''' '''
@ -118,13 +113,13 @@ class Blacklist(object):
f.close() f.close()
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
name=dict(required=True), name=dict(type='str', required=True),
state=dict(required=False, choices=['present', 'absent'], state=dict(type='str', default='present', choices=['absent', 'present']),
default='present'), blacklist_file=dict(type='str')
blacklist_file=dict(required=False, default=None)
), ),
supports_check_mode=True, supports_check_mode=True,
) )

@ -1,51 +1,46 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: locale_gen module: locale_gen
short_description: Creates or removes locales. short_description: Creates or removes locales
description: description:
- Manages locales by editing /etc/locale.gen and invoking locale-gen. - Manages locales by editing /etc/locale.gen and invoking locale-gen.
version_added: "1.6" version_added: "1.6"
author: "Augustus Kling (@AugustusKling)" author:
- Augustus Kling (@AugustusKling)
options: options:
name: name:
description: description:
- Name and encoding of the locale, such as "en_GB.UTF-8". - Name and encoding of the locale, such as "en_GB.UTF-8".
required: true required: true
default: null
aliases: []
state: state:
description: description:
- Whether the locale shall be present. - Whether the locale shall be present.
required: false choices: [ absent, present ]
choices: ["present", "absent"] default: present
default: "present"
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Ensure a locale exists. - name: Ensure a locale exists
- locale_gen: locale_gen:
name: de_CH.UTF-8 name: de_CH.UTF-8
state: present state: present
''' '''
import os import os
import os.path
from subprocess import Popen, PIPE, call
import re import re
from subprocess import Popen, PIPE, call
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.pycompat24 import get_exception from ansible.module_utils.pycompat24 import get_exception
@ -64,6 +59,7 @@ LOCALE_NORMALIZATION = {
".euctw": ".EUC-TW", ".euctw": ".EUC-TW",
} }
# =========================================== # ===========================================
# location module specific support methods. # location module specific support methods.
# #
@ -89,12 +85,14 @@ def is_available(name, ubuntuMode):
fd.close() fd.close()
return False return False
def is_present(name): def is_present(name):
"""Checks if the given locale is currently installed.""" """Checks if the given locale is currently installed."""
output = Popen(["locale", "-a"], stdout=PIPE).communicate()[0] output = Popen(["locale", "-a"], stdout=PIPE).communicate()[0]
output = to_native(output) output = to_native(output)
return any(fix_case(name) == fix_case(line) for line in output.splitlines()) return any(fix_case(name) == fix_case(line) for line in output.splitlines())
def fix_case(name): def fix_case(name):
"""locale -a might return the encoding in either lower or upper case. """locale -a might return the encoding in either lower or upper case.
Passing through this function makes them uniform for comparisons.""" Passing through this function makes them uniform for comparisons."""
@ -102,6 +100,7 @@ def fix_case(name):
name = name.replace(s, r) name = name.replace(s, r)
return name return name
def replace_line(existing_line, new_line): def replace_line(existing_line, new_line):
"""Replaces lines in /etc/locale.gen""" """Replaces lines in /etc/locale.gen"""
try: try:
@ -115,6 +114,7 @@ def replace_line(existing_line, new_line):
finally: finally:
f.close() f.close()
def set_locale(name, enabled=True): def set_locale(name, enabled=True):
""" Sets the state of the locale. Defaults to enabled. """ """ Sets the state of the locale. Defaults to enabled. """
search_string = '#{0,1}\s*%s (?P<charset>.+)' % name search_string = '#{0,1}\s*%s (?P<charset>.+)' % name
@ -133,6 +133,7 @@ def set_locale(name, enabled=True):
finally: finally:
f.close() f.close()
def apply_change(targetState, name): def apply_change(targetState, name):
"""Create or remove locale. """Create or remove locale.
@ -140,7 +141,7 @@ def apply_change(targetState, name):
targetState -- Desired state, either present or absent. targetState -- Desired state, either present or absent.
name -- Name including encoding such as de_CH.UTF-8. name -- Name including encoding such as de_CH.UTF-8.
""" """
if targetState=="present": if targetState == "present":
# Create locale. # Create locale.
set_locale(name, enabled=True) set_locale(name, enabled=True)
else: else:
@ -148,8 +149,9 @@ def apply_change(targetState, name):
set_locale(name, enabled=False) set_locale(name, enabled=False)
localeGenExitValue = call("locale-gen") localeGenExitValue = call("locale-gen")
if localeGenExitValue!=0: if localeGenExitValue != 0:
raise EnvironmentError(localeGenExitValue, "locale.gen failed to execute, it returned "+str(localeGenExitValue)) raise EnvironmentError(localeGenExitValue, "locale.gen failed to execute, it returned " + str(localeGenExitValue))
def apply_change_ubuntu(targetState, name): def apply_change_ubuntu(targetState, name):
"""Create or remove locale. """Create or remove locale.
@ -158,7 +160,7 @@ def apply_change_ubuntu(targetState, name):
targetState -- Desired state, either present or absent. targetState -- Desired state, either present or absent.
name -- Name including encoding such as de_CH.UTF-8. name -- Name including encoding such as de_CH.UTF-8.
""" """
if targetState=="present": if targetState == "present":
# Create locale. # Create locale.
# Ubuntu's patched locale-gen automatically adds the new locale to /var/lib/locales/supported.d/local # Ubuntu's patched locale-gen automatically adds the new locale to /var/lib/locales/supported.d/local
localeGenExitValue = call(["locale-gen", name]) localeGenExitValue = call(["locale-gen", name])
@ -181,20 +183,17 @@ def apply_change_ubuntu(targetState, name):
# Please provide a patch if you know how to avoid regenerating the locales to keep! # Please provide a patch if you know how to avoid regenerating the locales to keep!
localeGenExitValue = call(["locale-gen", "--purge"]) localeGenExitValue = call(["locale-gen", "--purge"])
if localeGenExitValue!=0: if localeGenExitValue != 0:
raise EnvironmentError(localeGenExitValue, "locale.gen failed to execute, it returned "+str(localeGenExitValue)) raise EnvironmentError(localeGenExitValue, "locale.gen failed to execute, it returned " + str(localeGenExitValue))
# ==============================================================
# main
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec=dict(
name = dict(required=True), name=dict(type='str', required=True),
state = dict(choices=['present','absent'], default='present'), state=dict(type='str', default='present', choices=['absent', 'present']),
), ),
supports_check_mode=True supports_check_mode=True,
) )
name = module.params['name'] name = module.params['name']
@ -218,7 +217,7 @@ def main():
prev_state = "present" prev_state = "present"
else: else:
prev_state = "absent" prev_state = "absent"
changed = (prev_state!=state) changed = (prev_state != state)
if module.check_mode: if module.check_mode:
module.exit_json(changed=changed) module.exit_json(changed=changed)

@ -1,22 +1,21 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2013, Alexander Bulimov <lazywolf0@gmail.com> # Copyright: (c) 2013, Alexander Bulimov <lazywolf0@gmail.com>
# based on lvol module by Jeroen Hoekx <jeroen.hoekx@dsquare.be> # Based on lvol module by Jeroen Hoekx <jeroen.hoekx@dsquare.be>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
author: "Alexander Bulimov (@abulimov)" author:
- Alexander Bulimov (@abulimov)
module: lvg module: lvg
short_description: Configure LVM volume groups short_description: Configure LVM volume groups
description: description:
@ -31,61 +30,54 @@ options:
description: description:
- List of comma-separated devices to use as physical devices in this volume group. Required when creating or resizing volume group. - List of comma-separated devices to use as physical devices in this volume group. Required when creating or resizing volume group.
- The module will take care of running pvcreate if needed. - The module will take care of running pvcreate if needed.
required: false
pesize: pesize:
description: description:
- The size of the physical extent in megabytes. Must be a power of 2. - The size of the physical extent in megabytes. Must be a power of 2.
default: 4 default: 4
required: false
pv_options: pv_options:
description: description:
- Additional options to pass to C(pvcreate) when creating the volume group. - Additional options to pass to C(pvcreate) when creating the volume group.
default: null
required: false
version_added: "2.4" version_added: "2.4"
vg_options: vg_options:
description: description:
- Additional options to pass to C(vgcreate) when creating the volume group. - Additional options to pass to C(vgcreate) when creating the volume group.
default: null
required: false
version_added: "1.6" version_added: "1.6"
state: state:
choices: [ "present", "absent" ]
default: present
description: description:
- Control if the volume group exists. - Control if the volume group exists.
required: false choices: [ absent, present ]
default: present
force: force:
choices: [ "yes", "no" ]
default: "no"
description: description:
- If yes, allows to remove volume group with logical volumes. - If C(yes), allows to remove volume group with logical volumes.
required: false type: bool
default: 'no'
notes: notes:
- module does not modify PE size for already present volume group - This module does not modify PE size for already present volume group.
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Create a volume group on top of /dev/sda1 with physical extent size = 32MB. - name: Create a volume group on top of /dev/sda1 with physical extent size = 32MB
- lvg: lvg:
vg: vg.services vg: vg.services
pvs: /dev/sda1 pvs: /dev/sda1
pesize: 32 pesize: 32
# Create or resize a volume group on top of /dev/sdb1 and /dev/sdc5.
# If, for example, we already have VG vg.services on top of /dev/sdb1, # If, for example, we already have VG vg.services on top of /dev/sdb1,
# this VG will be extended by /dev/sdc5. Or if vg.services was created on # this VG will be extended by /dev/sdc5. Or if vg.services was created on
# top of /dev/sda5, we first extend it with /dev/sdb1 and /dev/sdc5, # top of /dev/sda5, we first extend it with /dev/sdb1 and /dev/sdc5,
# and then reduce by /dev/sda5. # and then reduce by /dev/sda5.
- lvg: - name: Create or resize a volume group on top of /dev/sdb1 and /dev/sdc5.
lvg:
vg: vg.services vg: vg.services
pvs: /dev/sdb1,/dev/sdc5 pvs: /dev/sdb1,/dev/sdc5
# Remove a volume group with name vg.services. - name: Remove a volume group with name vg.services
- lvg: lvg:
vg: vg.services vg: vg.services
state: absent state: absent
''' '''
import os import os
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
@ -102,6 +94,7 @@ def parse_vgs(data):
}) })
return vgs return vgs
def find_mapper_device_name(module, dm_device): def find_mapper_device_name(module, dm_device):
dmsetup_cmd = module.get_bin_path('dmsetup', True) dmsetup_cmd = module.get_bin_path('dmsetup', True)
mapper_prefix = '/dev/mapper/' mapper_prefix = '/dev/mapper/'
@ -111,6 +104,7 @@ def find_mapper_device_name(module, dm_device):
mapper_device = mapper_prefix + dm_name.rstrip() mapper_device = mapper_prefix + dm_name.rstrip()
return mapper_device return mapper_device
def parse_pvs(module, data): def parse_pvs(module, data):
pvs = [] pvs = []
dm_prefix = '/dev/dm-' dm_prefix = '/dev/dm-'
@ -124,16 +118,17 @@ def parse_pvs(module, data):
}) })
return pvs return pvs
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec=dict(
vg=dict(required=True), vg=dict(type='str', required=True),
pvs=dict(type='list'), pvs=dict(type='list'),
pesize=dict(type='int', default=4), pesize=dict(type='int', default=4),
pv_options=dict(default=''), pv_options=dict(type='str', default=''),
vg_options=dict(default=''), vg_options=dict(type='str', default=''),
state=dict(choices=["absent", "present"], default='present'), state=dict(type='str', default='present', choices=['absent', 'present']),
force=dict(type='bool', default='no'), force=dict(type='bool', default=False),
), ),
supports_check_mode=True, supports_check_mode=True,
) )
@ -155,29 +150,29 @@ def main():
for idx, dev in enumerate(dev_list): for idx, dev in enumerate(dev_list):
dev_list[idx] = os.path.realpath(dev) dev_list[idx] = os.path.realpath(dev)
if state=='present': if state == 'present':
### check given devices # check given devices
for test_dev in dev_list: for test_dev in dev_list:
if not os.path.exists(test_dev): if not os.path.exists(test_dev):
module.fail_json(msg="Device %s not found."%test_dev) module.fail_json(msg="Device %s not found." % test_dev)
### get pv list # get pv list
pvs_cmd = module.get_bin_path('pvs', True) pvs_cmd = module.get_bin_path('pvs', True)
rc,current_pvs,err = module.run_command("%s --noheadings -o pv_name,vg_name --separator ';'" % pvs_cmd) rc, current_pvs, err = module.run_command("%s --noheadings -o pv_name,vg_name --separator ';'" % pvs_cmd)
if rc != 0: if rc != 0:
module.fail_json(msg="Failed executing pvs command.",rc=rc, err=err) module.fail_json(msg="Failed executing pvs command.", rc=rc, err=err)
### check pv for devices # check pv for devices
pvs = parse_pvs(module, current_pvs) pvs = parse_pvs(module, current_pvs)
used_pvs = [ pv for pv in pvs if pv['name'] in dev_list and pv['vg_name'] and pv['vg_name'] != vg ] used_pvs = [pv for pv in pvs if pv['name'] in dev_list and pv['vg_name'] and pv['vg_name'] != vg]
if used_pvs: if used_pvs:
module.fail_json(msg="Device %s is already in %s volume group."%(used_pvs[0]['name'],used_pvs[0]['vg_name'])) module.fail_json(msg="Device %s is already in %s volume group." % (used_pvs[0]['name'], used_pvs[0]['vg_name']))
vgs_cmd = module.get_bin_path('vgs', True) vgs_cmd = module.get_bin_path('vgs', True)
rc,current_vgs,err = module.run_command("%s --noheadings -o vg_name,pv_count,lv_count --separator ';'" % vgs_cmd) rc, current_vgs, err = module.run_command("%s --noheadings -o vg_name,pv_count,lv_count --separator ';'" % vgs_cmd)
if rc != 0: if rc != 0:
module.fail_json(msg="Failed executing vgs command.",rc=rc, err=err) module.fail_json(msg="Failed executing vgs command.", rc=rc, err=err)
changed = False changed = False
@ -192,42 +187,42 @@ def main():
if this_vg is None: if this_vg is None:
if state == 'present': if state == 'present':
### create VG # create VG
if module.check_mode: if module.check_mode:
changed = True changed = True
else: else:
### create PV # create PV
pvcreate_cmd = module.get_bin_path('pvcreate', True) pvcreate_cmd = module.get_bin_path('pvcreate', True)
for current_dev in dev_list: for current_dev in dev_list:
rc,_,err = module.run_command([pvcreate_cmd] + pvoptions + ['-f', str(current_dev)]) rc, _, err = module.run_command([pvcreate_cmd] + pvoptions + ['-f', str(current_dev)])
if rc == 0: if rc == 0:
changed = True changed = True
else: else:
module.fail_json(msg="Creating physical volume '%s' failed" % current_dev, rc=rc, err=err) module.fail_json(msg="Creating physical volume '%s' failed" % current_dev, rc=rc, err=err)
vgcreate_cmd = module.get_bin_path('vgcreate') vgcreate_cmd = module.get_bin_path('vgcreate')
rc,_,err = module.run_command([vgcreate_cmd] + vgoptions + ['-s', str(pesize), vg] + dev_list) rc, _, err = module.run_command([vgcreate_cmd] + vgoptions + ['-s', str(pesize), vg] + dev_list)
if rc == 0: if rc == 0:
changed = True changed = True
else: else:
module.fail_json(msg="Creating volume group '%s' failed"%vg, rc=rc, err=err) module.fail_json(msg="Creating volume group '%s' failed" % vg, rc=rc, err=err)
else: else:
if state == 'absent': if state == 'absent':
if module.check_mode: if module.check_mode:
module.exit_json(changed=True) module.exit_json(changed=True)
else: else:
if this_vg['lv_count'] == 0 or force: if this_vg['lv_count'] == 0 or force:
### remove VG # remove VG
vgremove_cmd = module.get_bin_path('vgremove', True) vgremove_cmd = module.get_bin_path('vgremove', True)
rc,_,err = module.run_command("%s --force %s" % (vgremove_cmd, vg)) rc, _, err = module.run_command("%s --force %s" % (vgremove_cmd, vg))
if rc == 0: if rc == 0:
module.exit_json(changed=True) module.exit_json(changed=True)
else: else:
module.fail_json(msg="Failed to remove volume group %s"%(vg),rc=rc, err=err) module.fail_json(msg="Failed to remove volume group %s" % (vg), rc=rc, err=err)
else: else:
module.fail_json(msg="Refuse to remove non-empty volume group %s without force=yes"%(vg)) module.fail_json(msg="Refuse to remove non-empty volume group %s without force=yes" % (vg))
### resize VG # resize VG
current_devs = [ os.path.realpath(pv['name']) for pv in pvs if pv['vg_name'] == vg ] current_devs = [os.path.realpath(pv['name']) for pv in pvs if pv['vg_name'] == vg]
devs_to_remove = list(set(current_devs) - set(dev_list)) devs_to_remove = list(set(current_devs) - set(dev_list))
devs_to_add = list(set(dev_list) - set(current_devs)) devs_to_add = list(set(dev_list) - set(current_devs))
@ -237,31 +232,31 @@ def main():
else: else:
if devs_to_add: if devs_to_add:
devs_to_add_string = ' '.join(devs_to_add) devs_to_add_string = ' '.join(devs_to_add)
### create PV # create PV
pvcreate_cmd = module.get_bin_path('pvcreate', True) pvcreate_cmd = module.get_bin_path('pvcreate', True)
for current_dev in devs_to_add: for current_dev in devs_to_add:
rc,_,err = module.run_command([pvcreate_cmd] + pvoptions + ['-f', str(current_dev)]) rc, _, err = module.run_command([pvcreate_cmd] + pvoptions + ['-f', str(current_dev)])
if rc == 0: if rc == 0:
changed = True changed = True
else: else:
module.fail_json(msg="Creating physical volume '%s' failed"%current_dev, rc=rc, err=err) module.fail_json(msg="Creating physical volume '%s' failed" % current_dev, rc=rc, err=err)
### add PV to our VG # add PV to our VG
vgextend_cmd = module.get_bin_path('vgextend', True) vgextend_cmd = module.get_bin_path('vgextend', True)
rc,_,err = module.run_command("%s %s %s" % (vgextend_cmd, vg, devs_to_add_string)) rc, _, err = module.run_command("%s %s %s" % (vgextend_cmd, vg, devs_to_add_string))
if rc == 0: if rc == 0:
changed = True changed = True
else: else:
module.fail_json(msg="Unable to extend %s by %s."%(vg, devs_to_add_string),rc=rc,err=err) module.fail_json(msg="Unable to extend %s by %s." % (vg, devs_to_add_string), rc=rc, err=err)
### remove some PV from our VG # remove some PV from our VG
if devs_to_remove: if devs_to_remove:
devs_to_remove_string = ' '.join(devs_to_remove) devs_to_remove_string = ' '.join(devs_to_remove)
vgreduce_cmd = module.get_bin_path('vgreduce', True) vgreduce_cmd = module.get_bin_path('vgreduce', True)
rc,_,err = module.run_command("%s --force %s %s" % (vgreduce_cmd, vg, devs_to_remove_string)) rc, _, err = module.run_command("%s --force %s %s" % (vgreduce_cmd, vg, devs_to_remove_string))
if rc == 0: if rc == 0:
changed = True changed = True
else: else:
module.fail_json(msg="Unable to reduce %s by %s."%(vg, devs_to_remove_string),rc=rc,err=err) module.fail_json(msg="Unable to reduce %s by %s." % (vg, devs_to_remove_string), rc=rc, err=err)
module.exit_json(changed=changed) module.exit_json(changed=changed)

@ -1,23 +1,21 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2013, Jeroen Hoekx <jeroen.hoekx@dsquare.be>, Alexander Bulimov <lazywolf0@gmail.com> # Copyright: (c) 2013, Jeroen Hoekx <jeroen.hoekx@dsquare.be>, Alexander Bulimov <lazywolf0@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
author: author:
- "Jeroen Hoekx (@jhoekx)" - Jeroen Hoekx (@jhoekx)
- "Alexander Bulimov (@abulimov)" - Alexander Bulimov (@abulimov)
module: lvol module: lvol
short_description: Configure LVM logical volumes short_description: Configure LVM logical volumes
description: description:
@ -40,152 +38,147 @@ options:
Float values must begin with a digit. Float values must begin with a digit.
Resizing using percentage values was not supported prior to 2.1. Resizing using percentage values was not supported prior to 2.1.
state: state:
choices: [ "present", "absent" ]
default: present
description: description:
- Control if the logical volume exists. If C(present) and the - Control if the logical volume exists. If C(present) and the
volume does not already exist then the C(size) option is required. volume does not already exist then the C(size) option is required.
required: false choices: [ absent, present ]
default: present
active: active:
version_added: "2.2"
choices: [ "yes", "no" ]
default: "yes"
description: description:
- Whether the volume is activate and visible to the host. - Whether the volume is activate and visible to the host.
required: false type: bool
default: 'yes'
version_added: "2.2"
force: force:
version_added: "1.5"
choices: [ "yes", "no" ]
default: "no"
description: description:
- Shrink or remove operations of volumes requires this switch. Ensures that - Shrink or remove operations of volumes requires this switch. Ensures that
that filesystems get never corrupted/destroyed by mistake. that filesystems get never corrupted/destroyed by mistake.
required: false type: bool
default: 'no'
version_added: "1.5"
opts: opts:
version_added: "2.0"
description: description:
- Free-form options to be passed to the lvcreate command - Free-form options to be passed to the lvcreate command
version_added: "2.0"
snapshot: snapshot:
version_added: "2.1"
description: description:
- The name of the snapshot volume - The name of the snapshot volume
required: false version_added: "2.1"
pvs: pvs:
version_added: "2.2"
description: description:
- Comma separated list of physical volumes e.g. /dev/sda,/dev/sdb - Comma separated list of physical volumes e.g. /dev/sda,/dev/sdb
required: false
shrink:
version_added: "2.2" version_added: "2.2"
shrink:
description: description:
- shrink if current size is higher than size requested - shrink if current size is higher than size requested
required: false type: bool
default: yes default: 'yes'
version_added: "2.2"
notes: notes:
- Filesystems on top of the volume are not resized. - Filesystems on top of the volume are not resized.
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Create a logical volume of 512m. - name: Create a logical volume of 512m
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
size: 512 size: 512
# Create a logical volume of 512m with disks /dev/sda and /dev/sdb - name: Create a logical volume of 512m with disks /dev/sda and /dev/sdb
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
size: 512 size: 512
pvs: /dev/sda,/dev/sdb pvs: /dev/sda,/dev/sdb
# Create cache pool logical volume - name: Create cache pool logical volume
- lvol: lvol:
vg: firefly vg: firefly
lv: lvcache lv: lvcache
size: 512m size: 512m
opts: --type cache-pool opts: --type cache-pool
# Create a logical volume of 512g. - name: Create a logical volume of 512g.
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
size: 512g size: 512g
# Create a logical volume the size of all remaining space in the volume group - name: Create a logical volume the size of all remaining space in the volume group
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
size: 100%FREE size: 100%FREE
# Create a logical volume with special options - name: Create a logical volume with special options
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
size: 512g size: 512g
opts: -r 16 opts: -r 16
# Extend the logical volume to 1024m. - name: Extend the logical volume to 1024m.
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
size: 1024 size: 1024
# Extend the logical volume to consume all remaining space in the volume group - name: Extend the logical volume to consume all remaining space in the volume group
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
size: +100%FREE size: +100%FREE
# Extend the logical volume to take all remaining space of the PVs - name: Extend the logical volume to take all remaining space of the PVs
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
size: 100%PVS size: 100%PVS
# Resize the logical volume to % of VG - name: Resize the logical volume to % of VG
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
size: 80%VG size: 80%VG
force: yes force: yes
# Reduce the logical volume to 512m - name: Reduce the logical volume to 512m
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
size: 512 size: 512
force: yes force: yes
# Set the logical volume to 512m and do not try to shrink if size is lower than current one - name: Set the logical volume to 512m and do not try to shrink if size is lower than current one
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
size: 512 size: 512
shrink: no shrink: no
# Remove the logical volume. - name: Remove the logical volume.
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
state: absent state: absent
force: yes force: yes
# Create a snapshot volume of the test logical volume. - name: Create a snapshot volume of the test logical volume.
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
snapshot: snap1 snapshot: snap1
size: 100m size: 100m
# Deactivate a logical volume - name: Deactivate a logical volume
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
active: false active: false
# Create a deactivated logical volume - name: Create a deactivated logical volume
- lvol: lvol:
vg: firefly vg: firefly
lv: test lv: test
size: 512g size: 512g
@ -196,24 +189,25 @@ import re
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
decimal_point = re.compile(r"(\d+)") decimal_point = re.compile(r"(\d+)")
def mkversion(major, minor, patch): def mkversion(major, minor, patch):
return (1000 * 1000 * int(major)) + (1000 * int(minor)) + int(patch) return (1000 * 1000 * int(major)) + (1000 * int(minor)) + int(patch)
def parse_lvs(data): def parse_lvs(data):
lvs = [] lvs = []
for line in data.splitlines(): for line in data.splitlines():
parts = line.strip().split(';') parts = line.strip().split(';')
lvs.append({ lvs.append({
'name': parts[0].replace('[','').replace(']',''), 'name': parts[0].replace('[', '').replace(']', ''),
'size': int(decimal_point.match(parts[1]).group(1)), 'size': int(decimal_point.match(parts[1]).group(1)),
'active': (parts[2][4] == 'a') 'active': (parts[2][4] == 'a')
}) })
return lvs return lvs
def parse_vgs(data): def parse_vgs(data):
vgs = [] vgs = []
for line in data.splitlines(): for line in data.splitlines():
@ -241,15 +235,15 @@ def get_lvm_version(module):
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
vg=dict(required=True), vg=dict(type='str', required=True),
lv=dict(required=True), lv=dict(type='str', required=True),
size=dict(type='str'), size=dict(type='str'),
opts=dict(type='str'), opts=dict(type='str'),
state=dict(choices=["absent", "present"], default='present'), state=dict(type='str', default='present', choices=['absent', 'present']),
force=dict(type='bool', default='no'), force=dict(type='bool', default=False),
shrink=dict(type='bool', default='yes'), shrink=dict(type='bool', default=True),
active=dict(type='bool', default='yes'), active=dict(type='bool', default=True),
snapshot=dict(type='str', default=None), snapshot=dict(type='str'),
pvs=dict(type='str') pvs=dict(type='str')
), ),
supports_check_mode=True, supports_check_mode=True,
@ -259,7 +253,7 @@ def main():
version_found = get_lvm_version(module) version_found = get_lvm_version(module)
if version_found is None: if version_found is None:
module.fail_json(msg="Failed to get LVM version number") module.fail_json(msg="Failed to get LVM version number")
version_yesopt = mkversion(2, 2, 99) # First LVM with the "--yes" option version_yesopt = mkversion(2, 2, 99) # First LVM with the "--yes" option
if version_found >= version_yesopt: if version_found >= version_yesopt:
yesopt = "--yes" yesopt = "--yes"
else: else:
@ -307,7 +301,7 @@ def main():
size_opt = 'l' size_opt = 'l'
size_unit = '' size_unit = ''
if not '%' in size: if '%' not in size:
# LVCREATE(8) -L --size option unit # LVCREATE(8) -L --size option unit
if size[-1].lower() in 'bskmgtpe': if size[-1].lower() in 'bskmgtpe':
size_unit = size[-1].lower() size_unit = size[-1].lower()
@ -373,7 +367,7 @@ def main():
msg = '' msg = ''
if this_lv is None: if this_lv is None:
if state == 'present': if state == 'present':
### create LV # create LV
lvcreate_cmd = module.get_bin_path("lvcreate", required=True) lvcreate_cmd = module.get_bin_path("lvcreate", required=True)
if snapshot is not None: if snapshot is not None:
cmd = "%s %s %s -%s %s%s -s -n %s %s %s/%s" % (lvcreate_cmd, test_opt, yesopt, size_opt, size, size_unit, snapshot, opts, vg, lv) cmd = "%s %s %s -%s %s%s -s -n %s %s %s/%s" % (lvcreate_cmd, test_opt, yesopt, size_opt, size, size_unit, snapshot, opts, vg, lv)
@ -386,7 +380,7 @@ def main():
module.fail_json(msg="Creating logical volume '%s' failed" % lv, rc=rc, err=err) module.fail_json(msg="Creating logical volume '%s' failed" % lv, rc=rc, err=err)
else: else:
if state == 'absent': if state == 'absent':
### remove LV # remove LV
if not force: if not force:
module.fail_json(msg="Sorry, no removal of logical volume %s without force=yes." % (this_lv['name'])) module.fail_json(msg="Sorry, no removal of logical volume %s without force=yes." % (this_lv['name']))
lvremove_cmd = module.get_bin_path("lvremove", required=True) lvremove_cmd = module.get_bin_path("lvremove", required=True)
@ -400,12 +394,12 @@ def main():
pass pass
elif size_opt == 'l': elif size_opt == 'l':
### Resize LV based on % value # Resize LV based on % value
tool = None tool = None
size_free = this_vg['free'] size_free = this_vg['free']
if size_whole == 'VG' or size_whole == 'PVS': if size_whole == 'VG' or size_whole == 'PVS':
size_requested = size_percent * this_vg['size'] / 100 size_requested = size_percent * this_vg['size'] / 100
else: # size_whole == 'FREE': else: # size_whole == 'FREE':
size_requested = size_percent * this_vg['free'] / 100 size_requested = size_percent * this_vg['free'] / 100
if '+' in size: if '+' in size:
size_requested += this_lv['size'] size_requested += this_lv['size']
@ -415,7 +409,7 @@ def main():
else: else:
module.fail_json( module.fail_json(
msg="Logical Volume %s could not be extended. Not enough free space left (%s%s required / %s%s available)" % msg="Logical Volume %s could not be extended. Not enough free space left (%s%s required / %s%s available)" %
(this_lv['name'], (size_requested - this_lv['size']), unit, size_free, unit) (this_lv['name'], (size_requested - this_lv['size']), unit, size_free, unit)
) )
elif shrink and this_lv['size'] > size_requested + this_vg['ext_size']: # more than an extent too large elif shrink and this_lv['size'] > size_requested + this_vg['ext_size']: # more than an extent too large
if size_requested == 0: if size_requested == 0:
@ -433,7 +427,7 @@ def main():
module.fail_json(msg="Unable to resize %s to %s%s" % (lv, size, size_unit), rc=rc, err=err, out=out) module.fail_json(msg="Unable to resize %s to %s%s" % (lv, size, size_unit), rc=rc, err=err, out=out)
elif rc == 0: elif rc == 0:
changed = True changed = True
msg="Volume %s resized to %s%s" % (this_lv['name'], size_requested, unit) msg = "Volume %s resized to %s%s" % (this_lv['name'], size_requested, unit)
elif "matches existing size" in err: elif "matches existing size" in err:
module.exit_json(changed=False, vg=vg, lv=this_lv['name'], size=this_lv['size']) module.exit_json(changed=False, vg=vg, lv=this_lv['name'], size=this_lv['size'])
elif "not larger than existing size" in err: elif "not larger than existing size" in err:
@ -442,7 +436,7 @@ def main():
module.fail_json(msg="Unable to resize %s to %s%s" % (lv, size, size_unit), rc=rc, err=err) module.fail_json(msg="Unable to resize %s to %s%s" % (lv, size, size_unit), rc=rc, err=err)
else: else:
### resize LV based on absolute values # resize LV based on absolute values
tool = None tool = None
if int(size) > this_lv['size']: if int(size) > this_lv['size']:
tool = module.get_bin_path("lvextend", required=True) tool = module.get_bin_path("lvextend", required=True)

@ -5,14 +5,13 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: ohai module: ohai
@ -42,7 +41,7 @@ from ansible.module_utils.basic import AnsibleModule
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict() argument_spec=dict()
) )
cmd = ["/usr/bin/env", "ohai"] cmd = ["/usr/bin/env", "ohai"]
rc, out, err = module.run_command(cmd, check_rc=True) rc, out, err = module.run_command(cmd, check_rc=True)

@ -5,14 +5,13 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: open_iscsi module: open_iscsi
@ -113,19 +112,16 @@ import time
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
ISCSIADM = 'iscsiadm' ISCSIADM = 'iscsiadm'
def compare_nodelists(l1, l2): def compare_nodelists(l1, l2):
l1.sort() l1.sort()
l2.sort() l2.sort()
return l1 == l2 return l1 == l2
def iscsi_get_cached_nodes(module, portal=None): def iscsi_get_cached_nodes(module, portal=None):
cmd = '%s --mode node' % iscsiadm_cmd cmd = '%s --mode node' % iscsiadm_cmd
(rc, out, err) = module.run_command(cmd) (rc, out, err) = module.run_command(cmd)
@ -156,7 +152,6 @@ def iscsi_get_cached_nodes(module, portal=None):
def iscsi_discover(module, portal, port): def iscsi_discover(module, portal, port):
cmd = '%s --mode discovery --type sendtargets --portal %s:%s' % (iscsiadm_cmd, portal, port) cmd = '%s --mode discovery --type sendtargets --portal %s:%s' % (iscsiadm_cmd, portal, port)
(rc, out, err) = module.run_command(cmd) (rc, out, err) = module.run_command(cmd)
@ -165,7 +160,6 @@ def iscsi_discover(module, portal, port):
def target_loggedon(module, target): def target_loggedon(module, target):
cmd = '%s --mode session' % iscsiadm_cmd cmd = '%s --mode session' % iscsiadm_cmd
(rc, out, err) = module.run_command(cmd) (rc, out, err) = module.run_command(cmd)
@ -178,7 +172,6 @@ def target_loggedon(module, target):
def target_login(module, target): def target_login(module, target):
node_auth = module.params['node_auth'] node_auth = module.params['node_auth']
node_user = module.params['node_user'] node_user = module.params['node_user']
node_pass = module.params['node_pass'] node_pass = module.params['node_pass']
@ -201,7 +194,6 @@ def target_login(module, target):
def target_logout(module, target): def target_logout(module, target):
cmd = '%s --mode node --targetname %s --logout' % (iscsiadm_cmd, target) cmd = '%s --mode node --targetname %s --logout' % (iscsiadm_cmd, target)
(rc, out, err) = module.run_command(cmd) (rc, out, err) = module.run_command(cmd)
@ -210,7 +202,6 @@ def target_logout(module, target):
def target_device_node(module, target): def target_device_node(module, target):
# if anyone know a better way to find out which devicenodes get created for # if anyone know a better way to find out which devicenodes get created for
# a given target... # a given target...
@ -227,7 +218,6 @@ def target_device_node(module, target):
def target_isauto(module, target): def target_isauto(module, target):
cmd = '%s --mode node --targetname %s' % (iscsiadm_cmd, target) cmd = '%s --mode node --targetname %s' % (iscsiadm_cmd, target)
(rc, out, err) = module.run_command(cmd) (rc, out, err) = module.run_command(cmd)
@ -242,7 +232,6 @@ def target_isauto(module, target):
def target_setauto(module, target): def target_setauto(module, target):
cmd = '%s --mode node --targetname %s --op=update --name node.startup --value automatic' % (iscsiadm_cmd, target) cmd = '%s --mode node --targetname %s --op=update --name node.startup --value automatic' % (iscsiadm_cmd, target)
(rc, out, err) = module.run_command(cmd) (rc, out, err) = module.run_command(cmd)
@ -251,7 +240,6 @@ def target_setauto(module, target):
def target_setmanual(module, target): def target_setmanual(module, target):
cmd = '%s --mode node --targetname %s --op=update --name node.startup --value manual' % (iscsiadm_cmd, target) cmd = '%s --mode node --targetname %s --op=update --name node.startup --value manual' % (iscsiadm_cmd, target)
(rc, out, err) = module.run_command(cmd) (rc, out, err) = module.run_command(cmd)
@ -260,24 +248,23 @@ def target_setmanual(module, target):
def main(): def main():
# load ansible module object # load ansible module object
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec=dict(
# target # target
portal = dict(required=False, aliases=['ip']), portal=dict(required=False, aliases=['ip']),
port = dict(required=False, default=3260), port=dict(required=False, default=3260),
target = dict(required=False, aliases=['name', 'targetname']), target=dict(required=False, aliases=['name', 'targetname']),
node_auth = dict(required=False, default='CHAP'), node_auth=dict(required=False, default='CHAP'),
node_user = dict(required=False), node_user=dict(required=False),
node_pass = dict(required=False, no_log=True), node_pass=dict(required=False, no_log=True),
# actions # actions
login = dict(type='bool', aliases=['state']), login=dict(type='bool', aliases=['state']),
auto_node_startup = dict(type='bool', aliases=['automatic']), auto_node_startup=dict(type='bool', aliases=['automatic']),
discover = dict(type='bool', default=False), discover=dict(type='bool', default=False),
show_nodes = dict(type='bool', default=False) show_nodes=dict(type='bool', default=False)
), ),
required_together=[['discover_user', 'discover_pass'], required_together=[['discover_user', 'discover_pass'],
@ -307,7 +294,7 @@ def main():
if discover: if discover:
if portal is None: if portal is None:
module.fail_json(msg = "Need to specify at least the portal (ip) to discover") module.fail_json(msg="Need to specify at least the portal (ip) to discover")
elif check: elif check:
nodes = cached nodes = cached
else: else:
@ -322,7 +309,7 @@ def main():
if login is not None or automatic is not None: if login is not None or automatic is not None:
if target is None: if target is None:
if len(nodes) > 1: if len(nodes) > 1:
module.fail_json(msg = "Need to specify a target") module.fail_json(msg="Need to specify a target")
else: else:
target = nodes[0] target = nodes[0]
else: else:
@ -333,7 +320,7 @@ def main():
check_target = True check_target = True
break break
if not check_target: if not check_target:
module.fail_json(msg = "Specified target not found") module.fail_json(msg="Specified target not found")
if show_nodes: if show_nodes:
result['nodes'] = nodes result['nodes'] = nodes

@ -4,14 +4,13 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
module: openwrt_init module: openwrt_init
author: author:
@ -85,6 +84,7 @@ from ansible.module_utils._text import to_bytes, to_native
module = None module = None
init_script = None init_script = None
# =============================== # ===============================
# Check if service is enabled # Check if service is enabled
def is_enabled(): def is_enabled():
@ -93,6 +93,7 @@ def is_enabled():
return True return True
return False return False
# =========================================== # ===========================================
# Main control flow # Main control flow
@ -100,15 +101,15 @@ def main():
global module, init_script global module, init_script
# init # init
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec=dict(
name = dict(required=True, type='str', aliases=['service']), name=dict(required=True, type='str', aliases=['service']),
state = dict(choices=['started', 'stopped', 'restarted', 'reloaded'], type='str'), state=dict(choices=['started', 'stopped', 'restarted', 'reloaded'], type='str'),
enabled = dict(type='bool'), enabled=dict(type='bool'),
pattern = dict(required=False, default=None), pattern=dict(required=False, default=None),
), ),
supports_check_mode=True, supports_check_mode=True,
required_one_of=[['state', 'enabled']], required_one_of=[['state', 'enabled']],
) )
# initialize # initialize
service = module.params['name'] service = module.params['name']
@ -116,7 +117,7 @@ def main():
rc = 0 rc = 0
out = err = '' out = err = ''
result = { result = {
'name': service, 'name': service,
'changed': False, 'changed': False,
} }
@ -164,7 +165,7 @@ def main():
if rc == 0: if rc == 0:
lines = psout.split("\n") lines = psout.split("\n")
for line in lines: for line in lines:
if module.params['pattern'] in line and not "pattern=" in line: if module.params['pattern'] in line and "pattern=" not in line:
# so as to not confuse ./hacking/test-module # so as to not confuse ./hacking/test-module
running = True running = True
break break
@ -187,7 +188,7 @@ def main():
action = 'stop' action = 'stop'
result['changed'] = True result['changed'] = True
else: else:
action = module.params['state'][:-2] # remove 'ed' from restarted/reloaded action = module.params['state'][:-2] # remove 'ed' from restarted/reloaded
result['state'] = 'started' result['state'] = 'started'
result['changed'] = True result['changed'] = True
@ -197,8 +198,8 @@ def main():
if rc != 0: if rc != 0:
module.fail_json(msg="Unable to %s service %s: %s" % (action, service, err)) module.fail_json(msg="Unable to %s service %s: %s" % (action, service, err))
module.exit_json(**result) module.exit_json(**result)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

@ -5,14 +5,13 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'], 'status': ['stableinterface'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: osx_defaults module: osx_defaults
@ -118,15 +117,17 @@ from ansible.module_utils.six import binary_type, text_type
# exceptions --------------------------------------------------------------- {{{ # exceptions --------------------------------------------------------------- {{{
class OSXDefaultsException(Exception): class OSXDefaultsException(Exception):
pass pass
# /exceptions -------------------------------------------------------------- }}} # /exceptions -------------------------------------------------------------- }}}
# class MacDefaults -------------------------------------------------------- {{{ # class MacDefaults -------------------------------------------------------- {{{
class OSXDefaults(object): class OSXDefaults(object):
""" Class to manage Mac OS user defaults """ """ Class to manage Mac OS user defaults """
# init ---------------------------------------------------------------- {{{ # init ---------------------------------------------------------------- {{{
""" Initialize this module. Finds 'defaults' executable and preps the parameters """ """ Initialize this module. Finds 'defaults' executable and preps the parameters """
def __init__(self, **kwargs): def __init__(self, **kwargs):
# Initial var for storing current defaults value # Initial var for storing current defaults value
@ -157,6 +158,7 @@ class OSXDefaults(object):
# tools --------------------------------------------------------------- {{{ # tools --------------------------------------------------------------- {{{
""" Converts value to given type """ """ Converts value to given type """
def _convert_type(self, type, value): def _convert_type(self, type, value):
if type == "string": if type == "string":
@ -194,6 +196,7 @@ class OSXDefaults(object):
raise OSXDefaultsException('Type is not supported: {0}'.format(type)) raise OSXDefaultsException('Type is not supported: {0}'.format(type))
""" Returns a normalized list of commandline arguments based on the "host" attribute """ """ Returns a normalized list of commandline arguments based on the "host" attribute """
def _host_args(self): def _host_args(self):
if self.host is None: if self.host is None:
return [] return []
@ -203,10 +206,12 @@ class OSXDefaults(object):
return ['-host', self.host] return ['-host', self.host]
""" Returns a list containing the "defaults" executable and any common base arguments """ """ Returns a list containing the "defaults" executable and any common base arguments """
def _base_command(self): def _base_command(self):
return [self.executable] + self._host_args() return [self.executable] + self._host_args()
""" Converts array output from defaults to an list """ """ Converts array output from defaults to an list """
@staticmethod @staticmethod
def _convert_defaults_str_to_list(value): def _convert_defaults_str_to_list(value):
@ -221,10 +226,12 @@ class OSXDefaults(object):
value = [re.sub(',$', '', x.strip(' ')) for x in value] value = [re.sub(',$', '', x.strip(' ')) for x in value]
return value return value
# /tools -------------------------------------------------------------- }}} # /tools -------------------------------------------------------------- }}}
# commands ------------------------------------------------------------ {{{ # commands ------------------------------------------------------------ {{{
""" Reads value of this domain & key from defaults """ """ Reads value of this domain & key from defaults """
def read(self): def read(self):
# First try to find out the type # First try to find out the type
rc, out, err = self.module.run_command(self._base_command() + ["read-type", self.domain, self.key]) rc, out, err = self.module.run_command(self._base_command() + ["read-type", self.domain, self.key])
@ -258,6 +265,7 @@ class OSXDefaults(object):
self.current_value = self._convert_type(type, out) self.current_value = self._convert_type(type, out)
""" Writes value to this domain & key to defaults """ """ Writes value to this domain & key to defaults """
def write(self): def write(self):
# We need to convert some values so the defaults commandline understands it # We need to convert some values so the defaults commandline understands it
@ -289,6 +297,7 @@ class OSXDefaults(object):
raise OSXDefaultsException('An error occurred while writing value to defaults: ' + out) raise OSXDefaultsException('An error occurred while writing value to defaults: ' + out)
""" Deletes defaults key from domain """ """ Deletes defaults key from domain """
def delete(self): def delete(self):
rc, out, err = self.module.run_command(self._base_command() + ['delete', self.domain, self.key]) rc, out, err = self.module.run_command(self._base_command() + ['delete', self.domain, self.key])
if rc != 0: if rc != 0:
@ -298,6 +307,7 @@ class OSXDefaults(object):
# run ----------------------------------------------------------------- {{{ # run ----------------------------------------------------------------- {{{
""" Does the magic! :) """ """ Does the magic! :) """
def run(self): def run(self):
# Get the current value from defaults # Get the current value from defaults
@ -319,10 +329,9 @@ class OSXDefaults(object):
# Current value matches the given value. Nothing need to be done. Arrays need extra care # Current value matches the given value. Nothing need to be done. Arrays need extra care
if self.type == "array" and self.current_value is not None and not self.array_add and \ if self.type == "array" and self.current_value is not None and not self.array_add and \
set(self.current_value) == set(self.value): set(self.current_value) == set(self.value):
return False return False
elif self.type == "array" and self.current_value is not None and self.array_add and \ elif self.type == "array" and self.current_value is not None and self.array_add and len(list(set(self.value) - set(self.current_value))) == 0:
len(list(set(self.value) - set(self.current_value))) == 0:
return False return False
elif self.current_value == self.value: elif self.current_value == self.value:
return False return False
@ -334,7 +343,8 @@ class OSXDefaults(object):
self.write() self.write()
return True return True
# /run ---------------------------------------------------------------- }}} # /run ---------------------------------------------------------------- }}}
# /class MacDefaults ------------------------------------------------------ }}} # /class MacDefaults ------------------------------------------------------ }}}
@ -410,6 +420,7 @@ def main():
except OSXDefaultsException as e: except OSXDefaultsException as e:
module.fail_json(msg=e.message) module.fail_json(msg=e.message)
# /main ------------------------------------------------------------------- }}} # /main ------------------------------------------------------------------- }}}
if __name__ == '__main__': if __name__ == '__main__':

@ -5,14 +5,13 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: pam_limits module: pam_limits
@ -133,38 +132,37 @@ from ansible.module_utils._text import to_native
def main(): def main():
pam_items = ['core', 'data', 'fsize', 'memlock', 'nofile', 'rss', 'stack', 'cpu', 'nproc', 'as', 'maxlogins', 'maxsyslogins', 'priority', 'locks', pam_items = ['core', 'data', 'fsize', 'memlock', 'nofile', 'rss', 'stack', 'cpu', 'nproc', 'as', 'maxlogins', 'maxsyslogins', 'priority', 'locks',
'sigpending', 'msgqueue', 'nice', 'rtprio', 'chroot'] 'sigpending', 'msgqueue', 'nice', 'rtprio', 'chroot']
pam_types = [ 'soft', 'hard', '-' ] pam_types = ['soft', 'hard', '-']
limits_conf = '/etc/security/limits.conf' limits_conf = '/etc/security/limits.conf'
module = AnsibleModule( module = AnsibleModule(
# not checking because of daisy chain to file module # not checking because of daisy chain to file module
argument_spec = dict( argument_spec=dict(
domain = dict(required=True, type='str'), domain=dict(required=True, type='str'),
limit_type = dict(required=True, type='str', choices=pam_types), limit_type=dict(required=True, type='str', choices=pam_types),
limit_item = dict(required=True, type='str', choices=pam_items), limit_item=dict(required=True, type='str', choices=pam_items),
value = dict(required=True, type='str'), value=dict(required=True, type='str'),
use_max = dict(default=False, type='bool'), use_max=dict(default=False, type='bool'),
use_min = dict(default=False, type='bool'), use_min=dict(default=False, type='bool'),
backup = dict(default=False, type='bool'), backup=dict(default=False, type='bool'),
dest = dict(default=limits_conf, type='str'), dest=dict(default=limits_conf, type='str'),
comment = dict(required=False, default='', type='str') comment=dict(required=False, default='', type='str')
) )
) )
domain = module.params['domain'] domain = module.params['domain']
limit_type = module.params['limit_type'] limit_type = module.params['limit_type']
limit_item = module.params['limit_item'] limit_item = module.params['limit_item']
value = module.params['value'] value = module.params['value']
use_max = module.params['use_max'] use_max = module.params['use_max']
use_min = module.params['use_min'] use_min = module.params['use_min']
backup = module.params['backup'] backup = module.params['backup']
limits_conf = module.params['dest'] limits_conf = module.params['dest']
new_comment = module.params['comment'] new_comment = module.params['comment']
changed = False changed = False
@ -192,7 +190,7 @@ def main():
space_pattern = re.compile(r'\s+') space_pattern = re.compile(r'\s+')
message = '' message = ''
f = open (limits_conf, 'rb') f = open(limits_conf, 'rb')
# Tempfile # Tempfile
nf = tempfile.NamedTemporaryFile(mode='w+') nf = tempfile.NamedTemporaryFile(mode='w+')
@ -211,9 +209,9 @@ def main():
continue continue
# Remove comment in line # Remove comment in line
newline = newline.split('#',1)[0] newline = newline.split('#', 1)[0]
try: try:
old_comment = line.split('#',1)[1] old_comment = line.split('#', 1)[1]
except: except:
old_comment = '' old_comment = ''
@ -228,10 +226,10 @@ def main():
nf.write(line) nf.write(line)
continue continue
line_domain = line_fields[0] line_domain = line_fields[0]
line_type = line_fields[1] line_type = line_fields[1]
line_item = line_fields[2] line_item = line_fields[2]
actual_value = line_fields[3] actual_value = line_fields[3]
if not (actual_value in ['unlimited', 'infinity', '-1'] or actual_value.isdigit()): if not (actual_value in ['unlimited', 'infinity', '-1'] or actual_value.isdigit()):
module.fail_json(msg="Invalid configuration of '%s'. Current value of %s is unsupported." % (limits_conf, line_item)) module.fail_json(msg="Invalid configuration of '%s'. Current value of %s is unsupported." % (limits_conf, line_item))
@ -280,7 +278,7 @@ def main():
if not found: if not found:
changed = True changed = True
if new_comment: if new_comment:
new_comment = "\t#"+new_comment new_comment = "\t#" + new_comment
new_limit = domain + "\t" + limit_type + "\t" + limit_item + "\t" + new_value + new_comment + "\n" new_limit = domain + "\t" + limit_type + "\t" + limit_item + "\t" + new_value + new_comment + "\n"
message = new_limit message = new_limit
nf.write(new_limit) nf.write(new_limit)
@ -297,7 +295,7 @@ def main():
pass pass
res_args = dict( res_args = dict(
changed = changed, msg = message changed=changed, msg=message
) )
if backup: if backup:

@ -4,14 +4,13 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'], 'status': ['stableinterface'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: puppet module: puppet
@ -130,16 +129,16 @@ def _get_facter_dir():
def _write_structured_data(basedir, basename, data): def _write_structured_data(basedir, basename, data):
if not os.path.exists(basedir): if not os.path.exists(basedir):
os.makedirs(basedir) os.makedirs(basedir)
file_path = os.path.join(basedir, "{0}.json".format(basename)) file_path = os.path.join(basedir, "{0}.json".format(basename))
# This is more complex than you might normally expect because we want to # This is more complex than you might normally expect because we want to
# open the file with only u+rw set. Also, we use the stat constants # open the file with only u+rw set. Also, we use the stat constants
# because ansible still supports python 2.4 and the octal syntax changed # because ansible still supports python 2.4 and the octal syntax changed
out_file = os.fdopen( out_file = os.fdopen(
os.open( os.open(
file_path, os.O_CREAT | os.O_WRONLY, file_path, os.O_CREAT | os.O_WRONLY,
stat.S_IRUSR | stat.S_IWUSR), 'w') stat.S_IRUSR | stat.S_IWUSR), 'w')
out_file.write(json.dumps(data).encode('utf8')) out_file.write(json.dumps(data).encode('utf8'))
out_file.close() out_file.close()
def main(): def main():
@ -217,8 +216,8 @@ def main():
cmd = ("%(base_cmd)s agent --onetime" cmd = ("%(base_cmd)s agent --onetime"
" --ignorecache --no-daemonize --no-usecacheonfailure --no-splay" " --ignorecache --no-daemonize --no-usecacheonfailure --no-splay"
" --detailed-exitcodes --verbose --color 0") % dict( " --detailed-exitcodes --verbose --color 0") % dict(
base_cmd=base_cmd, base_cmd=base_cmd,
) )
if p['puppetmaster']: if p['puppetmaster']:
cmd += " --server %s" % pipes.quote(p['puppetmaster']) cmd += " --server %s" % pipes.quote(p['puppetmaster'])
if p['show_diff']: if p['show_diff']:

@ -1,36 +1,33 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# (c) 2015, Brian Coca <bcoca@ansible.com> # Copyright: (c) 2015, Brian Coca <bcoca@ansible.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'], 'status': ['stableinterface'],
'supported_by': 'community'} 'supported_by': 'community'}
# This is a modification of @bcoca's `svc` module # This is a modification of @bcoca's `svc` module
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: runit module: runit
author: "James Sumners (@jsumners)" author:
- James Sumners (@jsumners)
version_added: "2.3" version_added: "2.3"
short_description: Manage runit services. short_description: Manage runit services
description: description:
- Controls runit services on remote hosts using the sv utility. - Controls runit services on remote hosts using the sv utility.
options: options:
name: name:
required: true
description: description:
- Name of the service to manage. - Name of the service to manage.
required: yes
state: state:
required: false
choices: [ started, stopped, restarted, killed, reloaded, once ]
description: description:
- C(started)/C(stopped) are idempotent actions that will not run - C(started)/C(stopped) are idempotent actions that will not run
commands unless necessary. C(restarted) will always bounce the commands unless necessary. C(restarted) will always bounce the
@ -38,51 +35,49 @@ options:
C(reloaded) will send a HUP (sv reload). C(reloaded) will send a HUP (sv reload).
C(once) will run a normally downed sv once (sv once), not really C(once) will run a normally downed sv once (sv once), not really
an idempotent operation. an idempotent operation.
choices: [ killed, once, reloaded, restarted, started, stopped ]
enabled: enabled:
required: false
choices: [ "yes", "no" ]
description: description:
- Wheater the service is enabled or not, if disabled it also implies stopped. - Wheater the service is enabled or not, if disabled it also implies stopped.
type: bool
service_dir: service_dir:
required: false
default: /var/service
description: description:
- directory runsv watches for services - directory runsv watches for services
default: /var/service
service_src: service_src:
required: false
default: /etc/sv
description: description:
- directory where services are defined, the source of symlinks to service_dir. - directory where services are defined, the source of symlinks to service_dir.
default: /etc/sv
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Example action to start sv dnscache, if not running - name: Start sv dnscache, if not running
- runit: runit:
name: dnscache name: dnscache
state: started state: started
# Example action to stop sv dnscache, if running - name: Stop sv dnscache, if running
- runit: runit:
name: dnscache name: dnscache
state: stopped state: stopped
# Example action to kill sv dnscache, in all cases - name: Kill sv dnscache, in all cases
- runit: runit:
name: dnscache name: dnscache
state: killed state: killed
# Example action to restart sv dnscache, in all cases - name: Restart sv dnscache, in all cases
- runit: runit:
name: dnscache name: dnscache
state: restarted state: restarted
# Example action to reload sv dnscache, in all cases - name: Reload sv dnscache, in all cases
- runit: runit:
name: dnscache name: dnscache
state: reloaded state: reloaded
# Example using alt sv directory location - name: Use alternative sv directory location
- runit: runit:
name: dnscache name: dnscache
state: reloaded state: reloaded
service_dir: /run/service service_dir: /run/service
@ -114,37 +109,35 @@ def _load_dist_subclass(cls, *args, **kwargs):
return super(cls, subclass).__new__(subclass) return super(cls, subclass).__new__(subclass)
class Sv(object): class Sv(object):
""" """
Main class that handles daemontools, can be subclassed and overridden in case Main class that handles daemontools, can be subclassed and overridden in case
we want to use a 'derivative' like encore, s6, etc we want to use a 'derivative' like encore, s6, etc
""" """
# def __new__(cls, *args, **kwargs):
#def __new__(cls, *args, **kwargs):
# return _load_dist_subclass(cls, args, kwargs) # return _load_dist_subclass(cls, args, kwargs)
def __init__(self, module): def __init__(self, module):
self.extra_paths = [ ] self.extra_paths = []
self.report_vars = ['state', 'enabled', 'svc_full', 'src_full', 'pid', 'duration', 'full_state'] self.report_vars = ['state', 'enabled', 'svc_full', 'src_full', 'pid', 'duration', 'full_state']
self.module = module self.module = module
self.name = module.params['name'] self.name = module.params['name']
self.service_dir = module.params['service_dir'] self.service_dir = module.params['service_dir']
self.service_src = module.params['service_src'] self.service_src = module.params['service_src']
self.enabled = None self.enabled = None
self.full_state = None self.full_state = None
self.state = None self.state = None
self.pid = None self.pid = None
self.duration = None self.duration = None
self.svc_cmd = module.get_bin_path('sv', opt_dirs=self.extra_paths, required=True) self.svc_cmd = module.get_bin_path('sv', opt_dirs=self.extra_paths, required=True)
self.svstat_cmd = module.get_bin_path('sv', opt_dirs=self.extra_paths) self.svstat_cmd = module.get_bin_path('sv', opt_dirs=self.extra_paths)
self.svc_full = '/'.join([ self.service_dir, self.name ]) self.svc_full = '/'.join([self.service_dir, self.name])
self.src_full = '/'.join([ self.service_src, self.name ]) self.src_full = '/'.join([self.service_src, self.name])
self.enabled = os.path.lexists(self.svc_full) self.enabled = os.path.lexists(self.svc_full)
if self.enabled: if self.enabled:
@ -152,7 +145,6 @@ class Sv(object):
else: else:
self.state = 'stopped' self.state = 'stopped'
def enable(self): def enable(self):
if os.path.exists(self.src_full): if os.path.exists(self.src_full):
try: try:
@ -163,7 +155,7 @@ class Sv(object):
self.module.fail_json(msg="Could not find source for service to enable (%s)." % self.src_full) self.module.fail_json(msg="Could not find source for service to enable (%s)." % self.src_full)
def disable(self): def disable(self):
self.execute_command([self.svc_cmd,'force-stop',self.src_full]) self.execute_command([self.svc_cmd, 'force-stop', self.src_full])
try: try:
os.unlink(self.svc_full) os.unlink(self.svc_full)
except OSError as e: except OSError as e:
@ -240,18 +232,16 @@ class Sv(object):
states[k] = self.__dict__[k] states[k] = self.__dict__[k]
return states return states
# ===========================================
# Main control flow
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec=dict(
name = dict(required=True), name=dict(type='str', required=True),
state = dict(choices=['started', 'stopped', 'restarted', 'killed', 'reloaded', 'once']), state=dict(type='str', choices=['killed', 'once', 'reloaded', 'restarted', 'started', 'stopped']),
enabled = dict(required=False, type='bool'), enabled=dict(type='bool'),
dist = dict(required=False, default='runit'), dist=dict(type='str', default='runit'),
service_dir = dict(required=False, default='/var/service'), service_dir=dict(type='str', default='/var/service'),
service_src = dict(required=False, default='/etc/sv'), service_src=dict(type='str', default='/etc/sv'),
), ),
supports_check_mode=True, supports_check_mode=True,
) )
@ -279,7 +269,7 @@ def main():
if state is not None and state != sv.state: if state is not None and state != sv.state:
changed = True changed = True
if not module.check_mode: if not module.check_mode:
getattr(sv,state)() getattr(sv, state)()
module.exit_json(changed=changed, sv=sv.report()) module.exit_json(changed=changed, sv=sv.report())

@ -42,8 +42,8 @@ author:
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Set (httpd_can_network_connect) flag on and keep it persistent across reboots - name: Set httpd_can_network_connect flag on and keep it persistent across reboots
- seboolean: seboolean:
name: httpd_can_network_connect name: httpd_can_network_connect
state: yes state: yes
persistent: yes persistent: yes
@ -173,7 +173,7 @@ def main():
argument_spec=dict( argument_spec=dict(
name=dict(type='str', required=True), name=dict(type='str', required=True),
persistent=dict(type='bool', default=False), persistent=dict(type='bool', default=False),
state=dict(type='bool', required=True) state=dict(type='bool', required=True),
), ),
supports_check_mode=True, supports_check_mode=True,
) )
@ -190,6 +190,7 @@ def main():
name = module.params['name'] name = module.params['name']
persistent = module.params['persistent'] persistent = module.params['persistent']
state = module.params['state'] state = module.params['state']
result = dict( result = dict(
name=name, name=name,
) )
@ -205,12 +206,11 @@ def main():
cur_value = get_boolean_value(module, name) cur_value = get_boolean_value(module, name)
if cur_value == state: if cur_value == state:
result['state'] = cur_value module.exit_json(changed=False, state=cur_value, **result)
result['changed'] = False
module.exit_json(**result)
if module.check_mode: if module.check_mode:
module.exit_json(changed=True) module.exit_json(changed=True)
if persistent: if persistent:
r = semanage_boolean_value(module, name, state) r = semanage_boolean_value(module, name, state)
else: else:
@ -223,6 +223,7 @@ def main():
selinux.security_commit_booleans() selinux.security_commit_booleans()
except: except:
module.fail_json(msg="Failed to commit pending boolean %s value" % name) module.fail_json(msg="Failed to commit pending boolean %s value" % name)
module.exit_json(**result) module.exit_json(**result)

@ -1,17 +1,15 @@
#!/usr/bin/python #!/usr/bin/python
# (c) 2014, Dan Keder <dan.keder@gmail.com> # Copyright: (c) 2014, Dan Keder <dan.keder@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: seport module: seport
@ -22,55 +20,55 @@ version_added: "2.0"
options: options:
ports: ports:
description: description:
- Ports or port ranges, separated by a comma - Ports or port ranges, separated by a comma.
required: true required: true
default: null
proto: proto:
description: description:
- Protocol for the specified port. - Protocol for the specified port.
required: true required: true
default: null choices: [ tcp, udp ]
choices: [ 'tcp', 'udp' ]
setype: setype:
description: description:
- SELinux type for the specified port. - SELinux type for the specified port.
required: true required: true
default: null
state: state:
description: description:
- Desired boolean value. - Desired boolean value.
required: true required: true
choices: [ absent, present ]
default: present default: present
choices: [ 'present', 'absent' ]
reload: reload:
description: description:
- Reload SELinux policy after commit. - Reload SELinux policy after commit.
required: false type: bool
default: yes default: 'yes'
notes: notes:
- The changes are persistent across reboots - The changes are persistent across reboots.
- Not tested on any debian based system - Not tested on any debian based system.
requirements: [ 'libselinux-python', 'policycoreutils-python' ] requirements:
author: Dan Keder - libselinux-python
- policycoreutils-python
author:
- Dan Keder
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Allow Apache to listen on tcp port 8888 - name: Allow Apache to listen on tcp port 8888
- seport: seport:
ports: 8888 ports: 8888
proto: tcp proto: tcp
setype: http_port_t setype: http_port_t
state: present state: present
# Allow sshd to listen on tcp port 8991 - name: Allow sshd to listen on tcp port 8991
- seport: seport:
ports: 8991 ports: 8991
proto: tcp proto: tcp
setype: ssh_port_t setype: ssh_port_t
state: present state: present
# Allow memcached to listen on tcp ports 10000-10100 and 10112 - name: Allow memcached to listen on tcp ports 10000-10100 and 10112
- seport: seport:
ports: 10000-10100,10112 ports: 10000-10100,10112
proto: tcp proto: tcp
setype: memcache_port_t setype: memcache_port_t
@ -81,15 +79,15 @@ import traceback
try: try:
import selinux import selinux
HAVE_SELINUX=True HAVE_SELINUX = True
except ImportError: except ImportError:
HAVE_SELINUX=False HAVE_SELINUX = False
try: try:
import seobject import seobject
HAVE_SEOBJECT=True HAVE_SEOBJECT = True
except ImportError: except ImportError:
HAVE_SEOBJECT=False HAVE_SEOBJECT = False
from ansible.module_utils.basic import AnsibleModule, HAVE_SELINUX from ansible.module_utils.basic import AnsibleModule, HAVE_SELINUX
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
@ -232,29 +230,16 @@ def semanage_port_del(module, ports, proto, setype, do_reload, sestore=''):
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec={ argument_spec=dict(
'ports': { ports=dict(type='str', required=True),
'required': True, proto=dict(type='str', required=True, choices=['tcp', 'udp']),
}, setype=dict(type='str', required=True),
'proto': { state=dict(type='str', required=True, choices=['absent', 'present']),
'required': True, reload=dict(type='bool', default=True),
'choices': ['tcp', 'udp'], ),
}, supports_check_mode=True,
'setype': {
'required': True,
},
'state': {
'required': True,
'choices': ['present', 'absent'],
},
'reload': {
'required': False,
'type': 'bool',
'default': 'yes',
},
},
supports_check_mode=True
) )
if not HAVE_SELINUX: if not HAVE_SELINUX:
module.fail_json(msg="This module requires libselinux-python") module.fail_json(msg="This module requires libselinux-python")

@ -959,7 +959,7 @@ class LinuxService(Service):
stdout = stdout1 + stdout2 stdout = stdout1 + stdout2
stderr = stderr1 + stderr2 stderr = stderr1 + stderr2
return(rc_state, stdout, stderr) return (rc_state, stdout, stderr)
class FreeBsdService(Service): class FreeBsdService(Service):

@ -6,12 +6,10 @@
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: solaris_zone module: solaris_zone
@ -20,12 +18,12 @@ description:
- Create, start, stop and delete Solaris zones. This module doesn't currently allow - Create, start, stop and delete Solaris zones. This module doesn't currently allow
changing of options for a zone that's already been created. changing of options for a zone that's already been created.
version_added: "2.0" version_added: "2.0"
author: Paul Markham author:
- Paul Markham
requirements: requirements:
- Solaris 10 or 11 - Solaris 10 or 11
options: options:
state: state:
required: true
description: description:
- C(present), configure and install the zone. - C(present), configure and install the zone.
- C(installed), synonym for C(present). - C(installed), synonym for C(present).
@ -37,8 +35,9 @@ options:
- C(configured), configure the ready so that it's to be attached. - C(configured), configure the ready so that it's to be attached.
- C(attached), attach a zone, but do not boot it. - C(attached), attach a zone, but do not boot it.
- C(detached), shutdown and detach a zone - C(detached), shutdown and detach a zone
choices: ['present', 'installed', 'started', 'running', 'stopped', 'absent', 'configured', 'attached', 'detached'] choices: [ absent, attached, configured, detached, installed, present, running, started, stopped ]
default: present default: present
required: true
name: name:
description: description:
- Zone name. - Zone name.
@ -47,48 +46,39 @@ options:
description: description:
- The path where the zone will be created. This is required when the zone is created, but not - The path where the zone will be created. This is required when the zone is created, but not
used otherwise. used otherwise.
required: false
default: null
sparse: sparse:
description: description:
- Whether to create a sparse (C(true)) or whole root (C(false)) zone. - Whether to create a sparse (C(true)) or whole root (C(false)) zone.
required: false type: bool
default: false default: 'no'
root_password: root_password:
description: description:
- The password hash for the root account. If not specified, the zone's root account - The password hash for the root account. If not specified, the zone's root account
will not have a password. will not have a password.
required: false
default: null
config: config:
description: description:
- 'The zonecfg configuration commands for this zone. See zonecfg(1M) for the valid options - 'The zonecfg configuration commands for this zone. See zonecfg(1M) for the valid options
and syntax. Typically this is a list of options separated by semi-colons or new lines, e.g. and syntax. Typically this is a list of options separated by semi-colons or new lines, e.g.
"set auto-boot=true;add net;set physical=bge0;set address=10.1.1.1;end"' "set auto-boot=true;add net;set physical=bge0;set address=10.1.1.1;end"'
required: false
default: empty string default: empty string
create_options: create_options:
description: description:
- 'Extra options to the zonecfg(1M) create command.' - 'Extra options to the zonecfg(1M) create command.'
required: false
default: empty string default: empty string
install_options: install_options:
description: description:
- 'Extra options to the zoneadm(1M) install command. To automate Solaris 11 zone creation, - 'Extra options to the zoneadm(1M) install command. To automate Solaris 11 zone creation,
use this to specify the profile XML file, e.g. install_options="-c sc_profile.xml"' use this to specify the profile XML file, e.g. install_options="-c sc_profile.xml"'
required: false
default: empty string default: empty string
attach_options: attach_options:
description: description:
- 'Extra options to the zoneadm attach command. For example, this can be used to specify - 'Extra options to the zoneadm attach command. For example, this can be used to specify
whether a minimum or full update of packages is required and if any packages need to whether a minimum or full update of packages is required and if any packages need to
be deleted. For valid values, see zoneadm(1M)' be deleted. For valid values, see zoneadm(1M)'
required: false
default: empty string default: empty string
timeout: timeout:
description: description:
- Timeout, in seconds, for zone to boot. - Timeout, in seconds, for zone to boot.
required: false
default: 600 default: 600
''' '''
@ -156,21 +146,21 @@ from ansible.module_utils.basic import AnsibleModule
class Zone(object): class Zone(object):
def __init__(self, module): def __init__(self, module):
self.changed = False self.changed = False
self.msg = [] self.msg = []
self.module = module self.module = module
self.path = self.module.params['path'] self.path = self.module.params['path']
self.name = self.module.params['name'] self.name = self.module.params['name']
self.sparse = self.module.params['sparse'] self.sparse = self.module.params['sparse']
self.root_password = self.module.params['root_password'] self.root_password = self.module.params['root_password']
self.timeout = self.module.params['timeout'] self.timeout = self.module.params['timeout']
self.config = self.module.params['config'] self.config = self.module.params['config']
self.create_options = self.module.params['create_options'] self.create_options = self.module.params['create_options']
self.install_options = self.module.params['install_options'] self.install_options = self.module.params['install_options']
self.attach_options = self.module.params['attach_options'] self.attach_options = self.module.params['attach_options']
self.zoneadm_cmd = self.module.get_bin_path('zoneadm', True) self.zoneadm_cmd = self.module.get_bin_path('zoneadm', True)
self.zonecfg_cmd = self.module.get_bin_path('zonecfg', True) self.zonecfg_cmd = self.module.get_bin_path('zonecfg', True)
self.ssh_keygen_cmd = self.module.get_bin_path('ssh-keygen', True) self.ssh_keygen_cmd = self.module.get_bin_path('ssh-keygen', True)
if self.module.check_mode: if self.module.check_mode:
@ -188,7 +178,7 @@ class Zone(object):
self.module.fail_json(msg='Missing required argument: path') self.module.fail_json(msg='Missing required argument: path')
if not self.module.check_mode: if not self.module.check_mode:
t = tempfile.NamedTemporaryFile(delete = False) t = tempfile.NamedTemporaryFile(delete=False)
if self.sparse: if self.sparse:
t.write('create %s\n' % self.create_options) t.write('create %s\n' % self.create_options)
@ -434,21 +424,23 @@ class Zone(object):
else: else:
self.msg.append('zone already attached') self.msg.append('zone already attached')
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
name=dict(required=True), name=dict(type='str', required=True),
state=dict(default='present', choices=['running', 'started', 'present', 'installed', 'stopped', 'absent', 'configured', 'detached', 'attached']), state=dict(type='str', default='present',
path=dict(default=None), choices=['absent', 'attached', 'configured', 'detached', 'installed', 'present', 'running', 'started', 'stopped']),
sparse=dict(default=False, type='bool'), path=dict(type='str'),
root_password=dict(default=None, no_log=True), sparse=dict(type='bool', default=False),
timeout=dict(default=600, type='int'), root_password=dict(type='str', no_log=True),
config=dict(default=''), timeout=dict(type='int', default=600),
create_options=dict(default=''), config=dict(type='str', default=''),
install_options=dict(default=''), create_options=dict(type='str', default=''),
attach_options=dict(default=''), install_options=dict(type='str', default=''),
attach_options=dict(type='str', default=''),
), ),
supports_check_mode=True supports_check_mode=True,
) )
zone = Zone(module) zone = Zone(module)

@ -1,34 +1,31 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# (c) 2015, Brian Coca <bcoca@ansible.com> # Copyright: (c) 2015, Brian Coca <bcoca@ansible.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'], 'status': ['stableinterface'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: svc module: svc
author: "Brian Coca (@bcoca)" author:
- Brian Coca (@bcoca)
version_added: "1.9" version_added: "1.9"
short_description: Manage daemontools services. short_description: Manage daemontools services
description: description:
- Controls daemontools services on remote hosts using the svc utility. - Controls daemontools services on remote hosts using the svc utility.
options: options:
name: name:
required: true
description: description:
- Name of the service to manage. - Name of the service to manage.
required: true
state: state:
required: false
choices: [ started, stopped, restarted, reloaded, once, killed ]
description: description:
- C(Started)/C(stopped) are idempotent actions that will not run - C(Started)/C(stopped) are idempotent actions that will not run
commands unless necessary. C(restarted) will always bounce the commands unless necessary. C(restarted) will always bounce the
@ -36,58 +33,55 @@ options:
C(reloaded) will send a sigusr1 (svc -1). C(reloaded) will send a sigusr1 (svc -1).
C(once) will run a normally downed svc once (svc -o), not really C(once) will run a normally downed svc once (svc -o), not really
an idempotent operation. an idempotent operation.
choices: [ killed, once, reloaded, restarted, started, stopped ]
downed: downed:
required: false
choices: [ "yes", "no" ]
default: no
description: description:
- Should a 'down' file exist or not, if it exists it disables auto startup. - Should a 'down' file exist or not, if it exists it disables auto startup.
defaults to no. Downed does not imply stopped. defaults to no. Downed does not imply stopped.
type: bool
default: 'no'
enabled: enabled:
required: false
choices: [ "yes", "no" ]
description: description:
- Wheater the service is enabled or not, if disabled it also implies stopped. - Wheater the service is enabled or not, if disabled it also implies stopped.
Make note that a service can be enabled and downed (no auto restart). Make note that a service can be enabled and downed (no auto restart).
type: bool
service_dir: service_dir:
required: false
default: /service
description: description:
- directory svscan watches for services - directory svscan watches for services
default: /service
service_src: service_src:
required: false
description: description:
- directory where services are defined, the source of symlinks to service_dir. - directory where services are defined, the source of symlinks to service_dir.
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Example action to start svc dnscache, if not running - name: Start svc dnscache, if not running
- svc: svc:
name: dnscache name: dnscache
state: started state: started
# Example action to stop svc dnscache, if running - name: Stop svc dnscache, if running
- svc: svc:
name: dnscache name: dnscache
state: stopped state: stopped
# Example action to kill svc dnscache, in all cases - name: Kill svc dnscache, in all cases
- svc: svc:
name: dnscache name: dnscache
state: killed state: killed
# Example action to restart svc dnscache, in all cases - name: Restart svc dnscache, in all cases
- svc: svc:
name: dnscache name: dnscache
state: restarted state: restarted
# Example action to reload svc dnscache, in all cases - name: Reload svc dnscache, in all cases
- svc: svc:
name: dnscache name: dnscache
state: reloaded state: reloaded
# Example using alt svc directory location - name: Using alternative svc directory location
- svc: svc:
name: dnscache name: dnscache
state: reloaded state: reloaded
service_dir: /var/service service_dir: /var/service
@ -119,38 +113,36 @@ def _load_dist_subclass(cls, *args, **kwargs):
return super(cls, subclass).__new__(subclass) return super(cls, subclass).__new__(subclass)
class Svc(object): class Svc(object):
""" """
Main class that handles daemontools, can be subclassed and overridden in case Main class that handles daemontools, can be subclassed and overridden in case
we want to use a 'derivative' like encore, s6, etc we want to use a 'derivative' like encore, s6, etc
""" """
# def __new__(cls, *args, **kwargs):
#def __new__(cls, *args, **kwargs):
# return _load_dist_subclass(cls, args, kwargs) # return _load_dist_subclass(cls, args, kwargs)
def __init__(self, module): def __init__(self, module):
self.extra_paths = [ '/command', '/usr/local/bin' ] self.extra_paths = ['/command', '/usr/local/bin']
self.report_vars = ['state', 'enabled', 'downed', 'svc_full', 'src_full', 'pid', 'duration', 'full_state'] self.report_vars = ['state', 'enabled', 'downed', 'svc_full', 'src_full', 'pid', 'duration', 'full_state']
self.module = module self.module = module
self.name = module.params['name'] self.name = module.params['name']
self.service_dir = module.params['service_dir'] self.service_dir = module.params['service_dir']
self.service_src = module.params['service_src'] self.service_src = module.params['service_src']
self.enabled = None self.enabled = None
self.downed = None self.downed = None
self.full_state = None self.full_state = None
self.state = None self.state = None
self.pid = None self.pid = None
self.duration = None self.duration = None
self.svc_cmd = module.get_bin_path('svc', opt_dirs=self.extra_paths) self.svc_cmd = module.get_bin_path('svc', opt_dirs=self.extra_paths)
self.svstat_cmd = module.get_bin_path('svstat', opt_dirs=self.extra_paths) self.svstat_cmd = module.get_bin_path('svstat', opt_dirs=self.extra_paths)
self.svc_full = '/'.join([ self.service_dir, self.name ]) self.svc_full = '/'.join([self.service_dir, self.name])
self.src_full = '/'.join([ self.service_src, self.name ]) self.src_full = '/'.join([self.service_src, self.name])
self.enabled = os.path.lexists(self.svc_full) self.enabled = os.path.lexists(self.svc_full)
if self.enabled: if self.enabled:
@ -160,7 +152,6 @@ class Svc(object):
self.downed = os.path.lexists('%s/down' % self.src_full) self.downed = os.path.lexists('%s/down' % self.src_full)
self.state = 'stopped' self.state = 'stopped'
def enable(self): def enable(self):
if os.path.exists(self.src_full): if os.path.exists(self.src_full):
try: try:
@ -175,11 +166,11 @@ class Svc(object):
os.unlink(self.svc_full) os.unlink(self.svc_full)
except OSError as e: except OSError as e:
self.module.fail_json(path=self.svc_full, msg='Error while unlinking: %s' % to_native(e)) self.module.fail_json(path=self.svc_full, msg='Error while unlinking: %s' % to_native(e))
self.execute_command([self.svc_cmd,'-dx',self.src_full]) self.execute_command([self.svc_cmd, '-dx', self.src_full])
src_log = '%s/log' % self.src_full src_log = '%s/log' % self.src_full
if os.path.exists(src_log): if os.path.exists(src_log):
self.execute_command([self.svc_cmd,'-dx',src_log]) self.execute_command([self.svc_cmd, '-dx', src_log])
def get_status(self): def get_status(self):
(rc, out, err) = self.execute_command([self.svstat_cmd, self.svc_full]) (rc, out, err) = self.execute_command([self.svstat_cmd, self.svc_full])
@ -245,19 +236,20 @@ class Svc(object):
states[k] = self.__dict__[k] states[k] = self.__dict__[k]
return states return states
# =========================================== # ===========================================
# Main control flow # Main control flow
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec=dict(
name = dict(required=True), name=dict(type='str', required=True),
state = dict(choices=['started', 'stopped', 'restarted', 'killed', 'reloaded', 'once']), state=dict(type='str', choices=['killed', 'once', 'reloaded', 'restarted', 'started', 'stopped']),
enabled = dict(required=False, type='bool'), enabled=dict(type='bool'),
downed = dict(required=False, type='bool'), downed=dict(type='bool'),
dist = dict(required=False, default='daemontools'), dist=dict(type='str', default='daemontools'),
service_dir = dict(required=False, default='/service'), service_dir=dict(type='str', default='/service'),
service_src = dict(required=False, default='/etc/service'), service_src=dict(type='str', default='/etc/service'),
), ),
supports_check_mode=True, supports_check_mode=True,
) )
@ -286,7 +278,7 @@ def main():
if state is not None and state != svc.state: if state is not None and state != svc.state:
changed = True changed = True
if not module.check_mode: if not module.check_mode:
getattr(svc,state[:-2])() getattr(svc, state[:-2])()
if downed is not None and downed != svc.downed: if downed is not None and downed != svc.downed:
changed = True changed = True
@ -303,7 +295,5 @@ def main():
module.exit_json(changed=changed, svc=svc.report()) module.exit_json(changed=changed, svc=svc.report())
if __name__ == '__main__': if __name__ == '__main__':
main() main()

@ -390,9 +390,8 @@ def main():
elif rc == 1: elif rc == 1:
# if not a user service and both init script and unit file exist stdout should have enabled/disabled, otherwise use rc entries # if not a user service and both init script and unit file exist stdout should have enabled/disabled, otherwise use rc entries
if not module.params['user'] and \ if not module.params['user'] and \
is_initd and \ is_initd and \
(not out.strip().endswith('disabled') or sysv_is_enabled(unit)): (not out.strip().endswith('disabled') or sysv_is_enabled(unit)):
enabled = True enabled = True
# default to current state # default to current state

@ -1,18 +1,16 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2016, Shinichi TAMURA (@tmshn) # Copyright: (c) 2016, Shinichi TAMURA (@tmshn)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: timezone module: timezone
@ -33,7 +31,6 @@ options:
- Name of the timezone for the system clock. - Name of the timezone for the system clock.
Default is to keep current setting. B(At least one of name and Default is to keep current setting. B(At least one of name and
hwclock are required.) hwclock are required.)
required: false
hwclock: hwclock:
description: description:
- Whether the hardware clock is in UTC or in local timezone. - Whether the hardware clock is in UTC or in local timezone.
@ -42,14 +39,13 @@ options:
to configure, especially on virtual environments such as AWS. to configure, especially on virtual environments such as AWS.
B(At least one of name and hwclock are required.) B(At least one of name and hwclock are required.)
I(Only used on Linux.) I(Only used on Linux.)
required: false aliases: [ rtc ]
aliases: ['rtc']
notes: notes:
- On SmartOS the C(sm-set-timezone) utility (part of the smtools package) is required to set the zone timezone - On SmartOS the C(sm-set-timezone) utility (part of the smtools package) is required to set the zone timezone
author: author:
- "Shinichi TAMURA (@tmshn)" - Shinichi TAMURA (@tmshn)
- "Jasper Lievisse Adriaanse (@jasperla)" - Jasper Lievisse Adriaanse (@jasperla)
- "Indrajit Raychaudhuri (@indrajitr)" - Indrajit Raychaudhuri (@indrajitr)
''' '''
RETURN = ''' RETURN = '''
@ -117,7 +113,7 @@ class Timezone(object):
# running in the global zone where changing the timezone has no effect. # running in the global zone where changing the timezone has no effect.
zonename_cmd = module.get_bin_path('zonename') zonename_cmd = module.get_bin_path('zonename')
if zonename_cmd is not None: if zonename_cmd is not None:
(rc, stdout, _ ) = module.run_command(zonename_cmd) (rc, stdout, _) = module.run_command(zonename_cmd)
if rc == 0 and stdout.strip() == 'global': if rc == 0 and stdout.strip() == 'global':
module.fail_json(msg='Adjusting timezone is not supported in Global Zone') module.fail_json(msg='Adjusting timezone is not supported in Global Zone')
@ -263,12 +259,12 @@ class SystemdTimezone(Timezone):
regexps = dict( regexps = dict(
hwclock=re.compile(r'^\s*RTC in local TZ\s*:\s*([^\s]+)', re.MULTILINE), hwclock=re.compile(r'^\s*RTC in local TZ\s*:\s*([^\s]+)', re.MULTILINE),
name =re.compile(r'^\s*Time ?zone\s*:\s*([^\s]+)', re.MULTILINE) name=re.compile(r'^\s*Time ?zone\s*:\s*([^\s]+)', re.MULTILINE)
) )
subcmds = dict( subcmds = dict(
hwclock='set-local-rtc', hwclock='set-local-rtc',
name ='set-timezone' name='set-timezone'
) )
def __init__(self, module): def __init__(self, module):
@ -316,7 +312,7 @@ class NosystemdTimezone(Timezone):
""" """
conf_files = dict( conf_files = dict(
name =None, # To be set in __init__ name=None, # To be set in __init__
hwclock=None, # To be set in __init__ hwclock=None, # To be set in __init__
adjtime='/etc/adjtime' adjtime='/etc/adjtime'
) )
@ -324,7 +320,7 @@ class NosystemdTimezone(Timezone):
allow_no_file = dict() allow_no_file = dict()
regexps = dict( regexps = dict(
name =None, # To be set in __init__ name=None, # To be set in __init__
hwclock=re.compile(r'^UTC\s*=\s*([^\s]+)', re.MULTILINE), hwclock=re.compile(r'^UTC\s*=\s*([^\s]+)', re.MULTILINE),
adjtime=re.compile(r'^(UTC|LOCAL)$', re.MULTILINE) adjtime=re.compile(r'^(UTC|LOCAL)$', re.MULTILINE)
) )
@ -334,32 +330,32 @@ class NosystemdTimezone(Timezone):
# Validate given timezone # Validate given timezone
if 'name' in self.value: if 'name' in self.value:
tzfile = self._verify_timezone() tzfile = self._verify_timezone()
self.update_timezone = ['%s %s /etc/localtime' % (self.module.get_bin_path('cp', required=True), tzfile)] self.update_timezone = ['%s %s /etc/localtime' % (self.module.get_bin_path('cp', required=True), tzfile)]
self.update_hwclock = self.module.get_bin_path('hwclock', required=True) self.update_hwclock = self.module.get_bin_path('hwclock', required=True)
self.allow_no_file['hwclock'] = True # Since this is only used for get values, file absense does not metter self.allow_no_file['hwclock'] = True # Since this is only used for get values, file absense does not metter
# Distribution-specific configurations # Distribution-specific configurations
if self.module.get_bin_path('dpkg-reconfigure') is not None: if self.module.get_bin_path('dpkg-reconfigure') is not None:
# Debian/Ubuntu # Debian/Ubuntu
# With additional hack for https://bugs.launchpad.net/ubuntu/+source/tzdata/+bug/1554806 # With additional hack for https://bugs.launchpad.net/ubuntu/+source/tzdata/+bug/1554806
self.update_timezone = ['rm -f /etc/localtime', '%s --frontend noninteractive tzdata' % self.update_timezone = ['rm -f /etc/localtime', '%s --frontend noninteractive tzdata' %
self.module.get_bin_path('dpkg-reconfigure', required=True)] self.module.get_bin_path('dpkg-reconfigure', required=True)]
self.conf_files['name'] = '/etc/timezone' self.conf_files['name'] = '/etc/timezone'
self.allow_no_file['name'] = True self.allow_no_file['name'] = True
self.conf_files['hwclock'] = '/etc/default/rcS' self.conf_files['hwclock'] = '/etc/default/rcS'
self.regexps['name'] = re.compile(r'^([^\s]+)', re.MULTILINE) self.regexps['name'] = re.compile(r'^([^\s]+)', re.MULTILINE)
self.tzline_format = '%s\n' self.tzline_format = '%s\n'
else: else:
# RHEL/CentOS # RHEL/CentOS
if self.module.get_bin_path('tzdata-update') is not None: if self.module.get_bin_path('tzdata-update') is not None:
self.update_timezone = [self.module.get_bin_path('tzdata-update', required=True)] self.update_timezone = [self.module.get_bin_path('tzdata-update', required=True)]
self.allow_no_file['name'] = True self.allow_no_file['name'] = True
# else: # else:
# self.update_timezone = 'cp ...' <- configured above # self.update_timezone = 'cp ...' <- configured above
# self.allow_no_file['name'] = False <- this is default behavior # self.allow_no_file['name'] = False <- this is default behavior
self.conf_files['name'] = '/etc/sysconfig/clock' self.conf_files['name'] = '/etc/sysconfig/clock'
self.conf_files['hwclock'] = '/etc/sysconfig/clock' self.conf_files['hwclock'] = '/etc/sysconfig/clock'
self.regexps['name'] = re.compile(r'^ZONE\s*=\s*"?([^"\s]+)"?', re.MULTILINE) self.regexps['name'] = re.compile(r'^ZONE\s*=\s*"?([^"\s]+)"?', re.MULTILINE)
self.tzline_format = 'ZONE="%s"\n' self.tzline_format = 'ZONE="%s"\n'
def _allow_ioerror(self, err, key): def _allow_ioerror(self, err, key):
# In some cases, even if the target file does not exist, # In some cases, even if the target file does not exist,
@ -535,7 +531,7 @@ class DarwinTimezone(Timezone):
""" """
regexps = dict( regexps = dict(
name = re.compile(r'^\s*Time ?Zone\s*:\s*([^\s]+)', re.MULTILINE) name=re.compile(r'^\s*Time ?Zone\s*:\s*([^\s]+)', re.MULTILINE)
) )
def __init__(self, module): def __init__(self, module):
@ -558,7 +554,7 @@ class DarwinTimezone(Timezone):
# Note: Skip the first line that contains the label 'Time Zones:' # Note: Skip the first line that contains the label 'Time Zones:'
out = self.execute(self.systemsetup, '-listtimezones').splitlines()[1:] out = self.execute(self.systemsetup, '-listtimezones').splitlines()[1:]
tz_list = list(map(lambda x: x.strip(), out)) tz_list = list(map(lambda x: x.strip(), out))
if not tz in tz_list: if tz not in tz_list:
self.abort('given timezone "%s" is not available' % tz) self.abort('given timezone "%s" is not available' % tz)
return tz return tz
@ -631,11 +627,13 @@ def main():
# Construct 'module' and 'tz' # Construct 'module' and 'tz'
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
hwclock=dict(choices=['UTC', 'local'], aliases=['rtc']), hwclock=dict(type='str', choices=['local', 'UTC'], aliases=['rtc']),
name=dict(), name=dict(type='str'),
), ),
required_one_of=[['hwclock', 'name']], required_one_of=[
supports_check_mode=True ['hwclock', 'name']
],
supports_check_mode=True,
) )
tz = Timezone(module) tz = Timezone(module)

@ -1,22 +1,19 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2014, Ahti Kitsik <ak@ahtik.com> # Copyright: (c) 2014, Ahti Kitsik <ak@ahtik.com>
# (c) 2014, Jarno Keskikangas <jarno.keskikangas@gmail.com> # Copyright: (c) 2014, Jarno Keskikangas <jarno.keskikangas@gmail.com>
# (c) 2013, Aleksey Ovcharenko <aleksey.ovcharenko@gmail.com> # Copyright: (c) 2013, Aleksey Ovcharenko <aleksey.ovcharenko@gmail.com>
# (c) 2013, James Martin <jmartin@basho.com> # Copyright: (c) 2013, James Martin <jmartin@basho.com>
#
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: ufw module: ufw
@ -25,9 +22,9 @@ description:
- Manage firewall with UFW. - Manage firewall with UFW.
version_added: 1.6 version_added: 1.6
author: author:
- "Aleksey Ovcharenko (@ovcharenko)" - Aleksey Ovcharenko (@ovcharenko)
- "Jarno Keskikangas (@pyykkis)" - Jarno Keskikangas (@pyykkis)
- "Ahti Kitsik (@ahtik)" - Ahti Kitsik (@ahtik)
notes: notes:
- See C(man ufw) for more examples. - See C(man ufw) for more examples.
requirements: requirements:
@ -39,99 +36,82 @@ options:
- C(disabled) unloads firewall and disables firewall on boot. - C(disabled) unloads firewall and disables firewall on boot.
- C(reloaded) reloads firewall. - C(reloaded) reloads firewall.
- C(reset) disables and resets firewall to installation defaults. - C(reset) disables and resets firewall to installation defaults.
required: false choices: [ disabled, enabled, reloaded, reset ]
choices: ['enabled', 'disabled', 'reloaded', 'reset']
policy: policy:
description: description:
- Change the default policy for incoming or outgoing traffic. - Change the default policy for incoming or outgoing traffic.
required: false aliases: [ default ]
aliases: ['default'] choices: [ allow, deny, reject ]
choices: ['allow', 'deny', 'reject']
direction: direction:
description: description:
- Select direction for a rule or default policy command. - Select direction for a rule or default policy command.
required: false choices: [ in, incoming, out, outgoing, routed ]
choices: ['in', 'out', 'incoming', 'outgoing', 'routed']
logging: logging:
description: description:
- Toggles logging. Logged packets use the LOG_KERN syslog facility. - Toggles logging. Logged packets use the LOG_KERN syslog facility.
choices: ['on', 'off', 'low', 'medium', 'high', 'full'] choices: [ on, off, low, medium, high, full ]
required: false
insert: insert:
description: description:
- Insert the corresponding rule as rule number NUM - Insert the corresponding rule as rule number NUM
required: false
rule: rule:
description: description:
- Add firewall rule - Add firewall rule
required: false choices: ['allow', 'deny', 'limit', 'reject']
choices: ['allow', 'deny', 'reject', 'limit']
log: log:
description: description:
- Log new connections matched to this rule - Log new connections matched to this rule
required: false type: bool
choices: ['yes', 'no']
from_ip: from_ip:
description: description:
- Source IP address. - Source IP address.
required: false aliases: [ from, src ]
aliases: ['from', 'src'] default: any
default: 'any'
from_port: from_port:
description: description:
- Source port. - Source port.
required: false
to_ip: to_ip:
description: description:
- Destination IP address. - Destination IP address.
required: false aliases: [ dest, to]
aliases: ['to', 'dest'] default: any
default: 'any'
to_port: to_port:
description: description:
- Destination port. - Destination port.
required: false aliases: [ port ]
aliases: ['port']
proto: proto:
description: description:
- TCP/IP protocol. - TCP/IP protocol.
choices: ['any', 'tcp', 'udp', 'ipv6', 'esp', 'ah'] choices: [ any, tcp, udp, ipv6, esp, ah ]
required: false
name: name:
description: description:
- Use profile located in C(/etc/ufw/applications.d) - Use profile located in C(/etc/ufw/applications.d).
required: false aliases: [ app ]
aliases: ['app']
delete: delete:
description: description:
- Delete rule. - Delete rule.
required: false type: bool
choices: ['yes', 'no']
interface: interface:
description: description:
- Specify interface for rule. - Specify interface for rule.
required: false aliases: [ if ]
aliases: ['if']
route: route:
description: description:
- Apply the rule to routed/forwarded packets. - Apply the rule to routed/forwarded packets.
required: false type: bool
choices: ['yes', 'no']
comment: comment:
description: description:
- Add a comment to the rule. Requires UFW version >=0.35. - Add a comment to the rule. Requires UFW version >=0.35.
required: false
version_added: "2.4" version_added: "2.4"
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Allow everything and enable UFW - name: Allow everything and enable UFW
- ufw: ufw:
state: enabled state: enabled
policy: allow policy: allow
# Set logging - name: Set logging
- ufw: ufw:
logging: on logging: on
# Sometimes it is desirable to let the sender know when traffic is # Sometimes it is desirable to let the sender know when traffic is
@ -159,30 +139,30 @@ EXAMPLES = '''
rule: allow rule: allow
name: OpenSSH name: OpenSSH
# Delete OpenSSH rule - name: Delete OpenSSH rule
- ufw: ufw:
rule: allow rule: allow
name: OpenSSH name: OpenSSH
delete: yes delete: yes
# Deny all access to port 53: - name: Deny all access to port 53
- ufw: ufw:
rule: deny rule: deny
port: 53 port: 53
# Allow port range 60000-61000 - name: Allow port range 60000-61000
- ufw: ufw:
rule: allow rule: allow
port: '60000:61000' port: 60000:61000
# Allow all access to tcp port 80: - name: Allow all access to tcp port 80
- ufw: ufw:
rule: allow rule: allow
port: 80 port: 80
proto: tcp proto: tcp
# Allow all access from RFC1918 networks to this host: - name: Allow all access from RFC1918 networks to this host
- ufw: ufw:
rule: allow rule: allow
src: '{{ item }}' src: '{{ item }}'
with_items: with_items:
@ -190,16 +170,16 @@ EXAMPLES = '''
- 172.16.0.0/12 - 172.16.0.0/12
- 192.168.0.0/16 - 192.168.0.0/16
# Deny access to udp port 514 from host 1.2.3.4 and include a comment: - name: Deny access to udp port 514 from host 1.2.3.4 and include a comment
- ufw: ufw:
rule: deny rule: deny
proto: udp proto: udp
src: 1.2.3.4 src: 1.2.3.4
port: 514 port: 514
comment: "Block syslog" comment: Block syslog
# Allow incoming access to eth0 from 1.2.3.5 port 5469 to 1.2.3.4 port 5469 - name: Allow incoming access to eth0 from 1.2.3.5 port 5469 to 1.2.3.4 port 5469
- ufw: ufw:
rule: allow rule: allow
interface: eth0 interface: eth0
direction: in direction: in
@ -209,17 +189,17 @@ EXAMPLES = '''
dest: 1.2.3.4 dest: 1.2.3.4
to_port: 5469 to_port: 5469
# Deny all traffic from the IPv6 2001:db8::/32 to tcp port 25 on this host.
# Note that IPv6 must be enabled in /etc/default/ufw for IPv6 firewalling to work. # Note that IPv6 must be enabled in /etc/default/ufw for IPv6 firewalling to work.
- ufw: - name: Deny all traffic from the IPv6 2001:db8::/32 to tcp port 25 on this host
ufw:
rule: deny rule: deny
proto: tcp proto: tcp
src: '2001:db8::/32' src: 2001:db8::/32
port: 25 port: 25
# Deny forwarded/routed traffic from subnet 1.2.3.0/24 to subnet 4.5.6.0/24.
# Can be used to further restrict a global FORWARD policy set to allow # Can be used to further restrict a global FORWARD policy set to allow
- ufw: - name: Deny forwarded/routed traffic from subnet 1.2.3.0/24 to subnet 4.5.6.0/24
ufw:
rule: deny rule: deny
route: yes route: yes
src: 1.2.3.0/24 src: 1.2.3.0/24
@ -234,27 +214,29 @@ from ansible.module_utils.basic import AnsibleModule
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec=dict(
state = dict(default=None, choices=['enabled', 'disabled', 'reloaded', 'reset']), state=dict(type='str', choices=['enabled', 'disabled', 'reloaded', 'reset']),
default = dict(default=None, aliases=['policy'], choices=['allow', 'deny', 'reject']), default=dict(type='str', aliases=['policy'], choices=['allow', 'deny', 'reject']),
logging = dict(default=None, choices=['on', 'off', 'low', 'medium', 'high', 'full']), logging=dict(type='str', choices=['full', 'high', 'low', 'medium', 'off', 'on']),
direction = dict(default=None, choices=['in', 'incoming', 'out', 'outgoing', 'routed']), direction=dict(type='str', choices=['in', 'incoming', 'out', 'outgoing', 'routed']),
delete = dict(default=False, type='bool'), delete=dict(type='bool', default=False),
route = dict(default=False, type='bool'), route=dict(type='bool', default=False),
insert = dict(default=None), insert=dict(type='str'),
rule = dict(default=None, choices=['allow', 'deny', 'reject', 'limit']), rule=dict(type='str', choices=['allow', 'deny', 'limit', 'reject']),
interface = dict(default=None, aliases=['if']), interface=dict(type='str', aliases=['if']),
log = dict(default=False, type='bool'), log=dict(type='bool', default=False),
from_ip = dict(default='any', aliases=['src', 'from']), from_ip=dict(type='str', default='any', aliases=['from', 'src']),
from_port = dict(default=None), from_port=dict(type='str'),
to_ip = dict(default='any', aliases=['dest', 'to']), to_ip=dict(type='str', default='any', aliases=['dest', 'to']),
to_port = dict(default=None, aliases=['port']), to_port=dict(type='str', aliases=['port']),
proto = dict(default=None, aliases=['protocol'], choices=['any', 'tcp', 'udp', 'ipv6', 'esp', 'ah']), proto=dict(type='str', aliases=['protocol'], choices=['ah', 'any', 'esp', 'ipv6', 'tcp', 'udp']),
app = dict(default=None, aliases=['name']), app=dict(type='str', aliases=['name']),
comment = dict(default=None, type='str') comment=dict(type='str'),
), ),
supports_check_mode = True, supports_check_mode=True,
mutually_exclusive = [['app', 'proto', 'logging']] mutually_exclusive=[
['app', 'proto', 'logging']
],
) )
cmds = [] cmds = []
@ -289,7 +271,7 @@ def main():
# Convert version to numbers # Convert version to numbers
major = int(matches.group(1)) major = int(matches.group(1))
minor = int(matches.group(2)) minor = int(matches.group(2))
rev = 0 rev = 0
if matches.group(3) is not None: if matches.group(3) is not None:
rev = int(matches.group(3)) rev = int(matches.group(3))
@ -304,7 +286,7 @@ def main():
if len(commands) < 1: if len(commands) < 1:
module.fail_json(msg="Not any of the command arguments %s given" % commands) module.fail_json(msg="Not any of the command arguments %s given" % commands)
if(params['interface'] is not None and params['direction'] is None): if (params['interface'] is not None and params['direction'] is None):
module.fail_json(msg="Direction must be specified when creating a rule on an interface") module.fail_json(msg="Direction must be specified when creating a rule on an interface")
# Ensure ufw is available # Ensure ufw is available
@ -319,8 +301,8 @@ def main():
cmd = [[ufw_bin], [module.check_mode, '--dry-run']] cmd = [[ufw_bin], [module.check_mode, '--dry-run']]
if command == 'state': if command == 'state':
states = { 'enabled': 'enable', 'disabled': 'disable', states = {'enabled': 'enable', 'disabled': 'disable',
'reloaded': 'reload', 'reset': 'reset' } 'reloaded': 'reload', 'reset': 'reset'}
execute(cmd + [['-f'], [states[value]]]) execute(cmd + [['-f'], [states[value]]])
elif command == 'logging': elif command == 'logging':
@ -343,10 +325,9 @@ def main():
cmd.append([params['interface'], "on %s" % params['interface']]) cmd.append([params['interface'], "on %s" % params['interface']])
cmd.append([module.boolean(params['log']), 'log']) cmd.append([module.boolean(params['log']), 'log'])
for (key, template) in [('from_ip', "from %s" ), ('from_port', "port %s" ), for (key, template) in [('from_ip', "from %s"), ('from_port', "port %s"),
('to_ip', "to %s" ), ('to_port', "port %s" ), ('to_ip', "to %s"), ('to_port', "port %s"),
('proto', "proto %s"), ('app', "app '%s'")]: ('proto', "proto %s"), ('app', "app '%s'")]:
value = params[key] value = params[key]
cmd.append([value, template % (value)]) cmd.append([value, template % (value)])

@ -1081,7 +1081,7 @@ class OpenBSDUser(User):
cmd.append(self.login_class) cmd.append(self.login_class)
if self.update_password == 'always' and self.password is not None \ if self.update_password == 'always' and self.password is not None \
and self.password != '*' and info[1] != self.password: and self.password != '*' and info[1] != self.password:
cmd.append('-p') cmd.append('-p')
cmd.append(self.password) cmd.append(self.password)
@ -1093,8 +1093,6 @@ class OpenBSDUser(User):
return self.execute_command(cmd) return self.execute_command(cmd)
# ===========================================
class NetBSDUser(User): class NetBSDUser(User):
""" """
This is a NetBSD User manipulation class. This is a NetBSD User manipulation class.
@ -1250,8 +1248,6 @@ class NetBSDUser(User):
return self.execute_command(cmd) return self.execute_command(cmd)
# ===========================================
class SunOS(User): class SunOS(User):
""" """
This is a SunOS User manipulation class - The main difference between This is a SunOS User manipulation class - The main difference between
@ -1613,7 +1609,7 @@ class DarwinUser(User):
(rc, out, err) = self.execute_command(cmd) (rc, out, err) = self.execute_command(cmd)
if rc != 0: if rc != 0:
self.module.fail_json(msg='Cannot %s user "%s" to group "%s".' self.module.fail_json(msg='Cannot %s user "%s" to group "%s".'
% (action, self.name, group), err=err, out=out, rc=rc) % (action, self.name, group), err=err, out=out, rc=rc)
return (rc, out, err) return (rc, out, err)
def _modify_group(self): def _modify_group(self):
@ -1681,7 +1677,7 @@ class DarwinUser(User):
return 0 return 0
else: else:
if self.name in hidden_users: if self.name in hidden_users:
del(hidden_users[hidden_users.index(self.name)]) del (hidden_users[hidden_users.index(self.name)])
cmd = ['defaults', 'write', plist_file, 'HiddenUsersList', '-array'] + hidden_users cmd = ['defaults', 'write', plist_file, 'HiddenUsersList', '-array'] + hidden_users
(rc, out, err) = self.execute_command(cmd) (rc, out, err) = self.execute_command(cmd)
@ -1784,7 +1780,7 @@ class DarwinUser(User):
if rc != 0: if rc != 0:
self.module.fail_json( self.module.fail_json(
msg='Cannot update property "%s" for user "%s".' msg='Cannot update property "%s" for user "%s".'
% (field[0], self.name), err=err, out=out, rc=rc) % (field[0], self.name), err=err, out=out, rc=rc)
changed = rc changed = rc
out += _out out += _out
err += _err err += _err
@ -2242,6 +2238,7 @@ def main():
module.exit_json(**result) module.exit_json(**result)
# import module snippets # import module snippets
if __name__ == '__main__': if __name__ == '__main__':
main() main()

@ -321,26 +321,3 @@ lib/ansible/modules/system/aix_inittab.py
lib/ansible/modules/system/capabilities.py lib/ansible/modules/system/capabilities.py
lib/ansible/modules/system/cronvar.py lib/ansible/modules/system/cronvar.py
lib/ansible/modules/system/crypttab.py lib/ansible/modules/system/crypttab.py
lib/ansible/modules/system/debconf.py
lib/ansible/modules/system/facter.py
lib/ansible/modules/system/filesystem.py
lib/ansible/modules/system/gconftool2.py
lib/ansible/modules/system/gluster_volume.py
lib/ansible/modules/system/group.py
lib/ansible/modules/system/java_cert.py
lib/ansible/modules/system/kernel_blacklist.py
lib/ansible/modules/system/locale_gen.py
lib/ansible/modules/system/lvg.py
lib/ansible/modules/system/lvol.py
lib/ansible/modules/system/ohai.py
lib/ansible/modules/system/open_iscsi.py
lib/ansible/modules/system/openwrt_init.py
lib/ansible/modules/system/osx_defaults.py
lib/ansible/modules/system/pam_limits.py
lib/ansible/modules/system/puppet.py
lib/ansible/modules/system/runit.py
lib/ansible/modules/system/seport.py
lib/ansible/modules/system/solaris_zone.py
lib/ansible/modules/system/svc.py
lib/ansible/modules/system/timezone.py
lib/ansible/modules/system/ufw.py

Loading…
Cancel
Save