mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-08 14:22:46 +00:00
Copying snapshot of extras modules
This commit is contained in:
committed by
Matt Clay
parent
8afa090417
commit
d4b117843a
140
lib/ansible/modules/extras/system/alternatives
Executable file
140
lib/ansible/modules/extras/system/alternatives
Executable file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Ansible module to manage symbolic link alternatives.
|
||||
(c) 2014, Gabe Mulley <gabe.mulley@gmail.com>
|
||||
|
||||
This file is part of Ansible
|
||||
|
||||
Ansible is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Ansible is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: alternatives
|
||||
short_description: Manages alternative programs for common commands
|
||||
description:
|
||||
- Manages symbolic links using the 'update-alternatives' tool provided on debian-like systems.
|
||||
- Useful when multiple programs are installed but provide similar functionality (e.g. different editors).
|
||||
version_added: "1.6"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The generic name of the link.
|
||||
required: true
|
||||
path:
|
||||
description:
|
||||
- The path to the real executable that the link should point to.
|
||||
required: true
|
||||
link:
|
||||
description:
|
||||
- The path to the symbolic link that should point to the real executable.
|
||||
required: false
|
||||
requirements: [ update-alternatives ]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: correct java version selected
|
||||
alternatives: name=java path=/usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java
|
||||
|
||||
- name: alternatives link created
|
||||
alternatives: name=hadoop-conf link=/etc/hadoop/conf path=/etc/hadoop/conf.ansible
|
||||
'''
|
||||
|
||||
DEFAULT_LINK_PRIORITY = 50
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
name = dict(required=True),
|
||||
path = dict(required=True),
|
||||
link = dict(required=False),
|
||||
)
|
||||
)
|
||||
|
||||
params = module.params
|
||||
name = params['name']
|
||||
path = params['path']
|
||||
link = params['link']
|
||||
|
||||
UPDATE_ALTERNATIVES = module.get_bin_path('update-alternatives',True)
|
||||
|
||||
current_path = None
|
||||
all_alternatives = []
|
||||
|
||||
(rc, query_output, query_error) = module.run_command(
|
||||
[UPDATE_ALTERNATIVES, '--query', name]
|
||||
)
|
||||
|
||||
# Gather the current setting and all alternatives from the query output.
|
||||
# Query output should look something like this:
|
||||
|
||||
# Name: java
|
||||
# Link: /usr/bin/java
|
||||
# Slaves:
|
||||
# java.1.gz /usr/share/man/man1/java.1.gz
|
||||
# Status: manual
|
||||
# Best: /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java
|
||||
# Value: /usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java
|
||||
|
||||
# Alternative: /usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java
|
||||
# Priority: 1061
|
||||
# Slaves:
|
||||
# java.1.gz /usr/lib/jvm/java-6-openjdk-amd64/jre/man/man1/java.1.gz
|
||||
|
||||
# Alternative: /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java
|
||||
# Priority: 1071
|
||||
# Slaves:
|
||||
# java.1.gz /usr/lib/jvm/java-7-openjdk-amd64/jre/man/man1/java.1.gz
|
||||
|
||||
if rc == 0:
|
||||
for line in query_output.splitlines():
|
||||
split_line = line.split(':')
|
||||
if len(split_line) == 2:
|
||||
key = split_line[0]
|
||||
value = split_line[1].strip()
|
||||
if key == 'Value':
|
||||
current_path = value
|
||||
elif key == 'Alternative':
|
||||
all_alternatives.append(value)
|
||||
elif key == 'Link' and not link:
|
||||
link = value
|
||||
|
||||
if current_path != path:
|
||||
try:
|
||||
# install the requested path if necessary
|
||||
if path not in all_alternatives:
|
||||
module.run_command(
|
||||
[UPDATE_ALTERNATIVES, '--install', link, name, path, str(DEFAULT_LINK_PRIORITY)],
|
||||
check_rc=True
|
||||
)
|
||||
|
||||
# select the requested path
|
||||
module.run_command(
|
||||
[UPDATE_ALTERNATIVES, '--set', name, path],
|
||||
check_rc=True
|
||||
)
|
||||
|
||||
module.exit_json(changed=True)
|
||||
except subprocess.CalledProcessError, cpe:
|
||||
module.fail_json(msg=str(dir(cpe)))
|
||||
else:
|
||||
module.exit_json(changed=False)
|
||||
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
||||
200
lib/ansible/modules/extras/system/at
Normal file
200
lib/ansible/modules/extras/system/at
Normal file
@@ -0,0 +1,200 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# (c) 2014, Richard Isaacson <richard.c.isaacson@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: at
|
||||
short_description: Schedule the execution of a command or script file via the at command.
|
||||
description:
|
||||
- Use this module to schedule a command or script file to run once in the future.
|
||||
- All jobs are executed in the 'a' queue.
|
||||
version_added: "1.5"
|
||||
options:
|
||||
command:
|
||||
description:
|
||||
- A command to be executed in the future.
|
||||
required: false
|
||||
default: null
|
||||
script_file:
|
||||
description:
|
||||
- An existing script file to be executed in the future.
|
||||
required: false
|
||||
default: null
|
||||
count:
|
||||
description:
|
||||
- The count of units in the future to execute the command or script file.
|
||||
required: true
|
||||
units:
|
||||
description:
|
||||
- The type of units in the future to execute the command or script file.
|
||||
required: true
|
||||
choices: ["minutes", "hours", "days", "weeks"]
|
||||
state:
|
||||
description:
|
||||
- The state dictates if the command or script file should be evaluated as present(added) or absent(deleted).
|
||||
required: false
|
||||
choices: ["present", "absent"]
|
||||
default: "present"
|
||||
unique:
|
||||
description:
|
||||
- If a matching job is present a new job will not be added.
|
||||
required: false
|
||||
default: false
|
||||
requirements:
|
||||
- at
|
||||
author: Richard Isaacson
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Schedule a command to execute in 20 minutes as root.
|
||||
- at: command="ls -d / > /dev/null" count=20 units="minutes"
|
||||
|
||||
# Match a command to an existing job and delete the job.
|
||||
- at: command="ls -d / > /dev/null" state="absent"
|
||||
|
||||
# Schedule a command to execute in 20 minutes making sure it is unique in the queue.
|
||||
- at: command="ls -d / > /dev/null" unique=true count=20 units="minutes"
|
||||
'''
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
|
||||
def add_job(module, result, at_cmd, count, units, command, script_file):
|
||||
at_command = "%s now + %s %s -f %s" % (at_cmd, count, units, script_file)
|
||||
rc, out, err = module.run_command(at_command, check_rc=True)
|
||||
if command:
|
||||
os.unlink(script_file)
|
||||
result['changed'] = True
|
||||
|
||||
|
||||
def delete_job(module, result, at_cmd, command, script_file):
|
||||
for matching_job in get_matching_jobs(module, at_cmd, script_file):
|
||||
at_command = "%s -d %s" % (at_cmd, matching_job)
|
||||
rc, out, err = module.run_command(at_command, check_rc=True)
|
||||
result['changed'] = True
|
||||
if command:
|
||||
os.unlink(script_file)
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def get_matching_jobs(module, at_cmd, script_file):
|
||||
matching_jobs = []
|
||||
|
||||
atq_cmd = module.get_bin_path('atq', True)
|
||||
|
||||
# Get list of job numbers for the user.
|
||||
atq_command = "%s" % atq_cmd
|
||||
rc, out, err = module.run_command(atq_command, check_rc=True)
|
||||
current_jobs = out.splitlines()
|
||||
if len(current_jobs) == 0:
|
||||
return matching_jobs
|
||||
|
||||
# Read script_file into a string.
|
||||
script_file_string = open(script_file).read().strip()
|
||||
|
||||
# Loop through the jobs.
|
||||
# If the script text is contained in a job add job number to list.
|
||||
for current_job in current_jobs:
|
||||
split_current_job = current_job.split()
|
||||
at_command = "%s -c %s" % (at_cmd, split_current_job[0])
|
||||
rc, out, err = module.run_command(at_command, check_rc=True)
|
||||
if script_file_string in out:
|
||||
matching_jobs.append(split_current_job[0])
|
||||
|
||||
# Return the list.
|
||||
return matching_jobs
|
||||
|
||||
|
||||
def create_tempfile(command):
|
||||
filed, script_file = tempfile.mkstemp(prefix='at')
|
||||
fileh = os.fdopen(filed, 'w')
|
||||
fileh.write(command)
|
||||
fileh.close()
|
||||
return script_file
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
command=dict(required=False,
|
||||
type='str'),
|
||||
script_file=dict(required=False,
|
||||
type='str'),
|
||||
count=dict(required=False,
|
||||
type='int'),
|
||||
units=dict(required=False,
|
||||
default=None,
|
||||
choices=['minutes', 'hours', 'days', 'weeks'],
|
||||
type='str'),
|
||||
state=dict(required=False,
|
||||
default='present',
|
||||
choices=['present', 'absent'],
|
||||
type='str'),
|
||||
unique=dict(required=False,
|
||||
default=False,
|
||||
type='bool')
|
||||
),
|
||||
mutually_exclusive=[['command', 'script_file']],
|
||||
required_one_of=[['command', 'script_file']],
|
||||
supports_check_mode=False
|
||||
)
|
||||
|
||||
at_cmd = module.get_bin_path('at', True)
|
||||
|
||||
command = module.params['command']
|
||||
script_file = module.params['script_file']
|
||||
count = module.params['count']
|
||||
units = module.params['units']
|
||||
state = module.params['state']
|
||||
unique = module.params['unique']
|
||||
|
||||
if (state == 'present') and (not count or not units):
|
||||
module.fail_json(msg="present state requires count and units")
|
||||
|
||||
result = {'state': state, 'changed': False}
|
||||
|
||||
# If command transform it into a script_file
|
||||
if command:
|
||||
script_file = create_tempfile(command)
|
||||
|
||||
# if absent remove existing and return
|
||||
if state == 'absent':
|
||||
delete_job(module, result, at_cmd, command, script_file)
|
||||
|
||||
# if unique if existing return unchanged
|
||||
if unique:
|
||||
if len(get_matching_jobs(module, at_cmd, script_file)) != 0:
|
||||
if command:
|
||||
os.unlink(script_file)
|
||||
module.exit_json(**result)
|
||||
|
||||
result['script_file'] = script_file
|
||||
result['count'] = count
|
||||
result['units'] = units
|
||||
|
||||
add_job(module, result, at_cmd, count, units, command, script_file)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
||||
187
lib/ansible/modules/extras/system/capabilities
Normal file
187
lib/ansible/modules/extras/system/capabilities
Normal file
@@ -0,0 +1,187 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2014, Nate Coraor <nate@bx.psu.edu>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: capabilities
|
||||
short_description: Manage Linux capabilities
|
||||
description:
|
||||
- This module manipulates files privileges using the Linux capabilities(7) system.
|
||||
version_added: "1.6"
|
||||
options:
|
||||
path:
|
||||
description:
|
||||
- Specifies the path to the file to be managed.
|
||||
required: true
|
||||
default: null
|
||||
capability:
|
||||
description:
|
||||
- Desired capability to set (with operator and flags, if state is C(present)) or remove (if state is C(absent))
|
||||
required: true
|
||||
default: null
|
||||
aliases: [ 'cap' ]
|
||||
state:
|
||||
description:
|
||||
- Whether the entry should be present or absent in the file's capabilities.
|
||||
choices: [ "present", "absent" ]
|
||||
default: present
|
||||
notes:
|
||||
- The capabilities system will automatically transform operators and flags
|
||||
into the effective set, so (for example, cap_foo=ep will probably become
|
||||
cap_foo+ep). This module does not attempt to determine the final operator
|
||||
and flags to compare, so you will want to ensure that your capabilities
|
||||
argument matches the final capabilities.
|
||||
requirements: []
|
||||
author: Nate Coraor <nate@bx.psu.edu>
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Set cap_sys_chroot+ep on /foo
|
||||
- capabilities: path=/foo capability=cap_sys_chroot+ep state=present
|
||||
|
||||
# Remove cap_net_bind_service from /bar
|
||||
- capabilities: path=/bar capability=cap_net_bind_service state=absent
|
||||
'''
|
||||
|
||||
|
||||
OPS = ( '=', '-', '+' )
|
||||
|
||||
# ==============================================================
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import re
|
||||
|
||||
class CapabilitiesModule(object):
|
||||
|
||||
platform = 'Linux'
|
||||
distribution = None
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.path = module.params['path'].strip()
|
||||
self.capability = module.params['capability'].strip().lower()
|
||||
self.state = module.params['state']
|
||||
self.getcap_cmd = module.get_bin_path('getcap', required=True)
|
||||
self.setcap_cmd = module.get_bin_path('setcap', required=True)
|
||||
self.capability_tup = self._parse_cap(self.capability, op_required=self.state=='present')
|
||||
|
||||
self.run()
|
||||
|
||||
def run(self):
|
||||
|
||||
current = self.getcap(self.path)
|
||||
caps = [ cap[0] for cap in current ]
|
||||
|
||||
if self.state == 'present' and self.capability_tup not in current:
|
||||
# need to add capability
|
||||
if self.module.check_mode:
|
||||
self.module.exit_json(changed=True, msg='capabilities changed')
|
||||
else:
|
||||
# remove from current cap list if it's already set (but op/flags differ)
|
||||
current = filter(lambda x: x[0] != self.capability_tup[0], current)
|
||||
# add new cap with correct op/flags
|
||||
current.append( self.capability_tup )
|
||||
self.module.exit_json(changed=True, state=self.state, msg='capabilities changed', stdout=self.setcap(self.path, current))
|
||||
elif self.state == 'absent' and self.capability_tup[0] in caps:
|
||||
# need to remove capability
|
||||
if self.module.check_mode:
|
||||
self.module.exit_json(changed=True, msg='capabilities changed')
|
||||
else:
|
||||
# remove from current cap list and then set current list
|
||||
current = filter(lambda x: x[0] != self.capability_tup[0], current)
|
||||
self.module.exit_json(changed=True, state=self.state, msg='capabilities changed', stdout=self.setcap(self.path, current))
|
||||
self.module.exit_json(changed=False, state=self.state)
|
||||
|
||||
def getcap(self, path):
|
||||
rval = []
|
||||
cmd = "%s -v %s" % (self.getcap_cmd, path)
|
||||
rc, stdout, stderr = self.module.run_command(cmd)
|
||||
# If file xattrs are set but no caps are set the output will be:
|
||||
# '/foo ='
|
||||
# If file xattrs are unset the output will be:
|
||||
# '/foo'
|
||||
# If the file does not eixst the output will be (with rc == 0...):
|
||||
# '/foo (No such file or directory)'
|
||||
if rc != 0 or (stdout.strip() != path and stdout.count(' =') != 1):
|
||||
self.module.fail_json(msg="Unable to get capabilities of %s" % path, stdout=stdout.strip(), stderr=stderr)
|
||||
if stdout.strip() != path:
|
||||
caps = stdout.split(' =')[1].strip().split()
|
||||
for cap in caps:
|
||||
cap = cap.lower()
|
||||
# getcap condenses capabilities with the same op/flags into a
|
||||
# comma-separated list, so we have to parse that
|
||||
if ',' in cap:
|
||||
cap_group = cap.split(',')
|
||||
cap_group[-1], op, flags = self._parse_cap(cap_group[-1])
|
||||
for subcap in cap_group:
|
||||
rval.append( ( subcap, op, flags ) )
|
||||
else:
|
||||
rval.append(self._parse_cap(cap))
|
||||
return rval
|
||||
|
||||
def setcap(self, path, caps):
|
||||
caps = ' '.join([ ''.join(cap) for cap in caps ])
|
||||
cmd = "%s '%s' %s" % (self.setcap_cmd, caps, path)
|
||||
rc, stdout, stderr = self.module.run_command(cmd)
|
||||
if rc != 0:
|
||||
self.module.fail_json(msg="Unable to set capabilities of %s" % path, stdout=stdout, stderr=stderr)
|
||||
else:
|
||||
return stdout
|
||||
|
||||
def _parse_cap(self, cap, op_required=True):
|
||||
opind = -1
|
||||
try:
|
||||
i = 0
|
||||
while opind == -1:
|
||||
opind = cap.find(OPS[i])
|
||||
i += 1
|
||||
except:
|
||||
if op_required:
|
||||
self.module.fail_json(msg="Couldn't find operator (one of: %s)" % str(OPS))
|
||||
else:
|
||||
return (cap, None, None)
|
||||
op = cap[opind]
|
||||
cap, flags = cap.split(op)
|
||||
return (cap, op, flags)
|
||||
|
||||
# ==============================================================
|
||||
# main
|
||||
|
||||
def main():
|
||||
|
||||
# defining module
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
path = dict(aliases=['key'], required=True),
|
||||
capability = dict(aliases=['cap'], required=True),
|
||||
state = dict(default='present', choices=['present', 'absent']),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
CapabilitiesModule(module)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
||||
170
lib/ansible/modules/extras/system/debconf
Normal file
170
lib/ansible/modules/extras/system/debconf
Normal file
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Ansible module to configure .deb packages.
|
||||
(c) 2014, Brian Coca <briancoca+ansible@gmail.com>
|
||||
|
||||
This file is part of Ansible
|
||||
|
||||
Ansible is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Ansible is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: debconf
|
||||
short_description: Configure a .deb package
|
||||
description:
|
||||
- Configure a .deb package using debconf-set-selections. Or just query
|
||||
existing selections.
|
||||
version_added: "1.6"
|
||||
notes:
|
||||
- This module requires the command line debconf tools.
|
||||
- A number of questions have to be answered (depending on the package).
|
||||
Use 'debconf-show <package>' on any Debian or derivative with the package
|
||||
installed to see questions/settings available.
|
||||
requirements: [ debconf, debconf-utils ]
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of package to configure.
|
||||
required: true
|
||||
default: null
|
||||
aliases: ['pkg']
|
||||
question:
|
||||
description:
|
||||
- A debconf configuration setting
|
||||
required: false
|
||||
default: null
|
||||
aliases: ['setting', 'selection']
|
||||
vtype:
|
||||
description:
|
||||
- The type of the value supplied
|
||||
required: false
|
||||
default: null
|
||||
choices: [string, password, boolean, select, multiselect, note, error, title, text]
|
||||
aliases: []
|
||||
value:
|
||||
description:
|
||||
- Value to set the configuration to
|
||||
required: false
|
||||
default: null
|
||||
aliases: ['answer']
|
||||
unseen:
|
||||
description:
|
||||
- Do not set 'seen' flag when pre-seeding
|
||||
required: false
|
||||
default: False
|
||||
aliases: []
|
||||
author: Brian Coca
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Set default locale to fr_FR.UTF-8
|
||||
debconf: name=locales question='locales/default_environment_locale' value=fr_FR.UTF-8 vtype='select'
|
||||
|
||||
# set to generate locales:
|
||||
debconf: name=locales question='locales/locales_to_be_generated' value='en_US.UTF-8 UTF-8, fr_FR.UTF-8 UTF-8' vtype='multiselect'
|
||||
|
||||
# Accept oracle license
|
||||
debconf: name='oracle-java7-installer' question='shared/accepted-oracle-license-v1-1' value='true' vtype='select'
|
||||
|
||||
# Specifying package you can register/return the list of questions and current values
|
||||
debconf: name='tzdata'
|
||||
'''
|
||||
|
||||
import pipes
|
||||
|
||||
def get_selections(module, pkg):
|
||||
cmd = [module.get_bin_path('debconf-show', True), pkg]
|
||||
rc, out, err = module.run_command(' '.join(cmd))
|
||||
|
||||
if rc != 0:
|
||||
module.fail_json(msg=err)
|
||||
|
||||
selections = {}
|
||||
|
||||
for line in out.splitlines():
|
||||
(key, value) = line.split(':', 1)
|
||||
selections[ key.strip('*').strip() ] = value.strip()
|
||||
|
||||
return selections
|
||||
|
||||
|
||||
def set_selection(module, pkg, question, vtype, value, unseen):
|
||||
|
||||
data = ' '.join([ question, vtype, value ])
|
||||
|
||||
setsel = module.get_bin_path('debconf-set-selections', True)
|
||||
cmd = ["echo %s %s |" % (pipes.quote(pkg), pipes.quote(data)), setsel]
|
||||
if unseen:
|
||||
cmd.append('-u')
|
||||
|
||||
return module.run_command(' '.join(cmd), use_unsafe_shell=True)
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
name = dict(required=True, aliases=['pkg'], type='str'),
|
||||
question = dict(required=False, aliases=['setting', 'selection'], type='str'),
|
||||
vtype = dict(required=False, type='str', choices=['string', 'password', 'boolean', 'select', 'multiselect', 'note', 'error', 'title', 'text']),
|
||||
value= dict(required=False, type='str'),
|
||||
unseen = dict(required=False, type='bool'),
|
||||
),
|
||||
required_together = ( ['question','vtype', 'value'],),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
#TODO: enable passing array of options and/or debconf file from get-selections dump
|
||||
pkg = module.params["name"]
|
||||
question = module.params["question"]
|
||||
vtype = module.params["vtype"]
|
||||
value = module.params["value"]
|
||||
unseen = module.params["unseen"]
|
||||
|
||||
prev = get_selections(module, pkg)
|
||||
diff = ''
|
||||
|
||||
changed = False
|
||||
msg = ""
|
||||
|
||||
if question is not 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")
|
||||
|
||||
if not question in prev or prev[question] != value:
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
if not module.check_mode:
|
||||
rc, msg, e = set_selection(module, pkg, question, vtype, value, unseen)
|
||||
if rc:
|
||||
module.fail_json(msg=e)
|
||||
|
||||
curr = { question: value }
|
||||
if question in prev:
|
||||
prev = {question: prev[question]}
|
||||
else:
|
||||
prev[question] = ''
|
||||
|
||||
module.exit_json(changed=changed, msg=msg, current=curr, previous=prev)
|
||||
|
||||
module.exit_json(changed=changed, msg=msg, current=prev)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
||||
56
lib/ansible/modules/extras/system/facter
Normal file
56
lib/ansible/modules/extras/system/facter
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: facter
|
||||
short_description: Runs the discovery program I(facter) on the remote system
|
||||
description:
|
||||
- Runs the I(facter) discovery program
|
||||
(U(https://github.com/puppetlabs/facter)) on the remote system, returning
|
||||
JSON data that can be useful for inventory purposes.
|
||||
version_added: "0.2"
|
||||
options: {}
|
||||
notes: []
|
||||
requirements: [ "facter", "ruby-json" ]
|
||||
author: Michael DeHaan
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Example command-line invocation
|
||||
ansible www.example.net -m facter
|
||||
'''
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict()
|
||||
)
|
||||
|
||||
cmd = ["/usr/bin/env", "facter", "--json"]
|
||||
rc, out, err = module.run_command(cmd, check_rc=True)
|
||||
module.exit_json(**json.loads(out))
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
||||
|
||||
119
lib/ansible/modules/extras/system/filesystem
Normal file
119
lib/ansible/modules/extras/system/filesystem
Normal file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Alexander Bulimov <lazywolf0@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
author: Alexander Bulimov
|
||||
module: filesystem
|
||||
short_description: Makes file system on block device
|
||||
description:
|
||||
- This module creates file system.
|
||||
version_added: "1.2"
|
||||
options:
|
||||
fstype:
|
||||
description:
|
||||
- File System type to be created.
|
||||
required: true
|
||||
dev:
|
||||
description:
|
||||
- Target block device.
|
||||
required: true
|
||||
force:
|
||||
choices: [ "yes", "no" ]
|
||||
default: "no"
|
||||
description:
|
||||
- If yes, allows to create new filesystem on devices that already has filesystem.
|
||||
required: false
|
||||
opts:
|
||||
description:
|
||||
- List of options to be passed to mkfs command.
|
||||
notes:
|
||||
- uses mkfs command
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Create a ext2 filesystem on /dev/sdb1.
|
||||
- filesystem: fstype=ext2 dev=/dev/sdb1
|
||||
|
||||
# Create a ext4 filesystem on /dev/sdb1 and check disk blocks.
|
||||
- filesystem: fstype=ext4 dev=/dev/sdb1 opts="-cc"
|
||||
'''
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
fstype=dict(required=True, aliases=['type']),
|
||||
dev=dict(required=True, aliases=['device']),
|
||||
opts=dict(),
|
||||
force=dict(type='bool', default='no'),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
dev = module.params['dev']
|
||||
fstype = module.params['fstype']
|
||||
opts = module.params['opts']
|
||||
force = module.boolean(module.params['force'])
|
||||
|
||||
changed = False
|
||||
|
||||
if not os.path.exists(dev):
|
||||
module.fail_json(msg="Device %s not found."%dev)
|
||||
|
||||
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))
|
||||
fs = raw_fs.strip()
|
||||
|
||||
|
||||
if fs == fstype:
|
||||
module.exit_json(changed=False)
|
||||
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)
|
||||
|
||||
### create fs
|
||||
|
||||
if module.check_mode:
|
||||
changed = True
|
||||
else:
|
||||
mkfs = module.get_bin_path('mkfs', required=True)
|
||||
cmd = None
|
||||
if fstype in ['ext2', 'ext3', 'ext4', 'ext4dev']:
|
||||
force_flag="-F"
|
||||
elif fstype in ['btrfs']:
|
||||
force_flag="-f"
|
||||
else:
|
||||
force_flag=""
|
||||
|
||||
if opts is None:
|
||||
cmd = "%s -t %s %s '%s'" % (mkfs, fstype, force_flag, dev)
|
||||
else:
|
||||
cmd = "%s -t %s %s %s '%s'" % (mkfs, fstype, force_flag, opts, dev)
|
||||
rc,_,err = module.run_command(cmd)
|
||||
if rc == 0:
|
||||
changed = True
|
||||
else:
|
||||
module.fail_json(msg="Creating filesystem %s on device '%s' failed"%(fstype,dev), rc=rc, err=err)
|
||||
|
||||
module.exit_json(changed=changed)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
||||
398
lib/ansible/modules/extras/system/firewalld
Normal file
398
lib/ansible/modules/extras/system/firewalld
Normal file
@@ -0,0 +1,398 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Adam Miller (maxamillion@fedoraproject.org)
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: firewalld
|
||||
short_description: Manage arbitrary ports/services with firewalld
|
||||
description:
|
||||
- This module allows for addition or deletion of services and ports either tcp or udp in either running or permanent firewalld rules
|
||||
version_added: "1.4"
|
||||
options:
|
||||
service:
|
||||
description:
|
||||
- "Name of a service to add/remove to/from firewalld - service must be listed in /etc/services"
|
||||
required: false
|
||||
default: null
|
||||
port:
|
||||
description:
|
||||
- "Name of a port to add/remove to/from firewalld must be in the form PORT/PROTOCOL"
|
||||
required: false
|
||||
default: null
|
||||
rich_rule:
|
||||
description:
|
||||
- "Rich rule to add/remove to/from firewalld"
|
||||
required: false
|
||||
default: null
|
||||
zone:
|
||||
description:
|
||||
- 'The firewalld zone to add/remove to/from (NOTE: default zone can be configured per system but "public" is default from upstream. Available choices can be extended based on per-system configs, listed here are "out of the box" defaults).'
|
||||
required: false
|
||||
default: system-default(public)
|
||||
choices: [ "work", "drop", "internal", "external", "trusted", "home", "dmz", "public", "block"]
|
||||
permanent:
|
||||
description:
|
||||
- "Should this configuration be in the running firewalld configuration or persist across reboots"
|
||||
required: true
|
||||
default: true
|
||||
state:
|
||||
description:
|
||||
- "Should this port accept(enabled) or reject(disabled) connections"
|
||||
required: true
|
||||
default: enabled
|
||||
timeout:
|
||||
description:
|
||||
- "The amount of time the rule should be in effect for when non-permanent"
|
||||
required: false
|
||||
default: 0
|
||||
notes:
|
||||
- Not tested on any debian based system
|
||||
requirements: [ firewalld >= 0.2.11 ]
|
||||
author: Adam Miller <maxamillion@fedoraproject.org>
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- firewalld: service=https permanent=true state=enabled
|
||||
- firewalld: port=8081/tcp permanent=true state=disabled
|
||||
- firewalld: zone=dmz service=http permanent=true state=enabled
|
||||
- firewalld: rich_rule='rule service name="ftp" audit limit value="1/m" accept' permanent=true state=enabled
|
||||
'''
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
try:
|
||||
import firewall.config
|
||||
FW_VERSION = firewall.config.VERSION
|
||||
|
||||
from firewall.client import FirewallClient
|
||||
fw = FirewallClient()
|
||||
if not fw.connected:
|
||||
raise Exception('failed to connect to the firewalld daemon')
|
||||
except ImportError:
|
||||
print "failed=True msg='firewalld required for this module'"
|
||||
sys.exit(1)
|
||||
except Exception, e:
|
||||
print "failed=True msg='%s'" % str(e)
|
||||
sys.exit(1)
|
||||
|
||||
################
|
||||
# port handling
|
||||
#
|
||||
def get_port_enabled(zone, port_proto):
|
||||
if port_proto in fw.getPorts(zone):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def set_port_enabled(zone, port, protocol, timeout):
|
||||
fw.addPort(zone, port, protocol, timeout)
|
||||
|
||||
def set_port_disabled(zone, port, protocol):
|
||||
fw.removePort(zone, port, protocol)
|
||||
|
||||
def get_port_enabled_permanent(zone, port_proto):
|
||||
fw_zone = fw.config().getZoneByName(zone)
|
||||
fw_settings = fw_zone.getSettings()
|
||||
if tuple(port_proto) in fw_settings.getPorts():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def set_port_enabled_permanent(zone, port, protocol):
|
||||
fw_zone = fw.config().getZoneByName(zone)
|
||||
fw_settings = fw_zone.getSettings()
|
||||
fw_settings.addPort(port, protocol)
|
||||
fw_zone.update(fw_settings)
|
||||
|
||||
def set_port_disabled_permanent(zone, port, protocol):
|
||||
fw_zone = fw.config().getZoneByName(zone)
|
||||
fw_settings = fw_zone.getSettings()
|
||||
fw_settings.removePort(port, protocol)
|
||||
fw_zone.update(fw_settings)
|
||||
|
||||
|
||||
####################
|
||||
# service handling
|
||||
#
|
||||
def get_service_enabled(zone, service):
|
||||
if service in fw.getServices(zone):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def set_service_enabled(zone, service, timeout):
|
||||
fw.addService(zone, service, timeout)
|
||||
|
||||
def set_service_disabled(zone, service):
|
||||
fw.removeService(zone, service)
|
||||
|
||||
def get_service_enabled_permanent(zone, service):
|
||||
fw_zone = fw.config().getZoneByName(zone)
|
||||
fw_settings = fw_zone.getSettings()
|
||||
if service in fw_settings.getServices():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def set_service_enabled_permanent(zone, service):
|
||||
fw_zone = fw.config().getZoneByName(zone)
|
||||
fw_settings = fw_zone.getSettings()
|
||||
fw_settings.addService(service)
|
||||
fw_zone.update(fw_settings)
|
||||
|
||||
def set_service_disabled_permanent(zone, service):
|
||||
fw_zone = fw.config().getZoneByName(zone)
|
||||
fw_settings = fw_zone.getSettings()
|
||||
fw_settings.removeService(service)
|
||||
fw_zone.update(fw_settings)
|
||||
|
||||
|
||||
####################
|
||||
# rich rule handling
|
||||
#
|
||||
def get_rich_rule_enabled(zone, rule):
|
||||
if rule in fw.getRichRules(zone):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def set_rich_rule_enabled(zone, rule, timeout):
|
||||
fw.addRichRule(zone, rule, timeout)
|
||||
|
||||
def set_rich_rule_disabled(zone, rule):
|
||||
fw.removeRichRule(zone, rule)
|
||||
|
||||
def get_rich_rule_enabled_permanent(zone, rule):
|
||||
fw_zone = fw.config().getZoneByName(zone)
|
||||
fw_settings = fw_zone.getSettings()
|
||||
if rule in fw_settings.getRichRules():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def set_rich_rule_enabled_permanent(zone, rule):
|
||||
fw_zone = fw.config().getZoneByName(zone)
|
||||
fw_settings = fw_zone.getSettings()
|
||||
fw_settings.addRichRule(rule)
|
||||
fw_zone.update(fw_settings)
|
||||
|
||||
def set_rich_rule_disabled_permanent(zone, rule):
|
||||
fw_zone = fw.config().getZoneByName(zone)
|
||||
fw_settings = fw_zone.getSettings()
|
||||
fw_settings.removeRichRule(rule)
|
||||
fw_zone.update(fw_settings)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
service=dict(required=False,default=None),
|
||||
port=dict(required=False,default=None),
|
||||
rich_rule=dict(required=False,default=None),
|
||||
zone=dict(required=False,default=None),
|
||||
permanent=dict(type='bool',required=True),
|
||||
state=dict(choices=['enabled', 'disabled'], required=True),
|
||||
timeout=dict(type='int',required=False,default=0),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
## Pre-run version checking
|
||||
if FW_VERSION < "0.2.11":
|
||||
module.fail_json(msg='unsupported version of firewalld, requires >= 2.0.11')
|
||||
|
||||
## Global Vars
|
||||
changed=False
|
||||
msgs = []
|
||||
service = module.params['service']
|
||||
rich_rule = module.params['rich_rule']
|
||||
|
||||
if module.params['port'] != None:
|
||||
port, protocol = module.params['port'].split('/')
|
||||
if protocol == None:
|
||||
module.fail_json(msg='improper port format (missing protocol?)')
|
||||
else:
|
||||
port = None
|
||||
|
||||
if module.params['zone'] != None:
|
||||
zone = module.params['zone']
|
||||
else:
|
||||
zone = fw.getDefaultZone()
|
||||
|
||||
permanent = module.params['permanent']
|
||||
desired_state = module.params['state']
|
||||
timeout = module.params['timeout']
|
||||
|
||||
## Check for firewalld running
|
||||
try:
|
||||
if fw.connected == False:
|
||||
module.fail_json(msg='firewalld service must be running')
|
||||
except AttributeError:
|
||||
module.fail_json(msg="firewalld connection can't be established,\
|
||||
version likely too old. Requires firewalld >= 2.0.11")
|
||||
|
||||
modification_count = 0
|
||||
if service != None:
|
||||
modification_count += 1
|
||||
if port != None:
|
||||
modification_count += 1
|
||||
if rich_rule != None:
|
||||
modification_count += 1
|
||||
|
||||
if modification_count > 1:
|
||||
module.fail_json(msg='can only operate on port, service or rich_rule at once')
|
||||
|
||||
if service != None:
|
||||
if permanent:
|
||||
is_enabled = get_service_enabled_permanent(zone, service)
|
||||
msgs.append('Permanent operation')
|
||||
|
||||
if desired_state == "enabled":
|
||||
if is_enabled == False:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
set_service_enabled_permanent(zone, service)
|
||||
changed=True
|
||||
elif desired_state == "disabled":
|
||||
if is_enabled == True:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
set_service_disabled_permanent(zone, service)
|
||||
changed=True
|
||||
else:
|
||||
is_enabled = get_service_enabled(zone, service)
|
||||
msgs.append('Non-permanent operation')
|
||||
|
||||
|
||||
if desired_state == "enabled":
|
||||
if is_enabled == False:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
set_service_enabled(zone, service, timeout)
|
||||
changed=True
|
||||
elif desired_state == "disabled":
|
||||
if is_enabled == True:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
set_service_disabled(zone, service)
|
||||
changed=True
|
||||
|
||||
if changed == True:
|
||||
msgs.append("Changed service %s to %s" % (service, desired_state))
|
||||
|
||||
if port != None:
|
||||
if permanent:
|
||||
is_enabled = get_port_enabled_permanent(zone, [port, protocol])
|
||||
msgs.append('Permanent operation')
|
||||
|
||||
if desired_state == "enabled":
|
||||
if is_enabled == False:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
set_port_enabled_permanent(zone, port, protocol)
|
||||
changed=True
|
||||
elif desired_state == "disabled":
|
||||
if is_enabled == True:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
set_port_disabled_permanent(zone, port, protocol)
|
||||
changed=True
|
||||
else:
|
||||
is_enabled = get_port_enabled(zone, [port,protocol])
|
||||
msgs.append('Non-permanent operation')
|
||||
|
||||
if desired_state == "enabled":
|
||||
if is_enabled == False:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
set_port_enabled(zone, port, protocol, timeout)
|
||||
changed=True
|
||||
elif desired_state == "disabled":
|
||||
if is_enabled == True:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
set_port_disabled(zone, port, protocol)
|
||||
changed=True
|
||||
|
||||
if changed == True:
|
||||
msgs.append("Changed port %s to %s" % ("%s/%s" % (port, protocol), \
|
||||
desired_state))
|
||||
|
||||
if rich_rule != None:
|
||||
if permanent:
|
||||
is_enabled = get_rich_rule_enabled_permanent(zone, rich_rule)
|
||||
msgs.append('Permanent operation')
|
||||
|
||||
if desired_state == "enabled":
|
||||
if is_enabled == False:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
set_rich_rule_enabled_permanent(zone, rich_rule)
|
||||
changed=True
|
||||
elif desired_state == "disabled":
|
||||
if is_enabled == True:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
set_rich_rule_disabled_permanent(zone, rich_rule)
|
||||
changed=True
|
||||
else:
|
||||
is_enabled = get_rich_rule_enabled(zone, rich_rule)
|
||||
msgs.append('Non-permanent operation')
|
||||
|
||||
if desired_state == "enabled":
|
||||
if is_enabled == False:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
set_rich_rule_enabled(zone, rich_rule, timeout)
|
||||
changed=True
|
||||
elif desired_state == "disabled":
|
||||
if is_enabled == True:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
set_rich_rule_disabled(zone, rich_rule)
|
||||
changed=True
|
||||
|
||||
if changed == True:
|
||||
msgs.append("Changed rich_rule %s to %s" % (rich_rule, desired_state))
|
||||
|
||||
module.exit_json(changed=changed, msg=', '.join(msgs))
|
||||
|
||||
|
||||
#################################################
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
||||
|
||||
143
lib/ansible/modules/extras/system/getent
Normal file
143
lib/ansible/modules/extras/system/getent
Normal file
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2014, Brian Coca <brian.coca+dev@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: getent
|
||||
short_description: a wrapper to the unix getent utility
|
||||
description:
|
||||
- Runs getent against one of it's various databases and returns information into
|
||||
the host's facts
|
||||
version_added: "1.8"
|
||||
options:
|
||||
database:
|
||||
required: True
|
||||
description:
|
||||
- the name of a getent database supported by the target system (passwd, group,
|
||||
hosts, etc).
|
||||
key:
|
||||
required: False
|
||||
default: ''
|
||||
description:
|
||||
- key from which to return values from the specified database, otherwise the
|
||||
full contents are returned.
|
||||
split:
|
||||
required: False
|
||||
default: None
|
||||
description:
|
||||
- "character used to split the database values into lists/arrays such as ':' or '\t', otherwise it will try to pick one depending on the database"
|
||||
fail_key:
|
||||
required: False
|
||||
default: True
|
||||
description:
|
||||
- If a supplied key is missing this will make the task fail if True
|
||||
|
||||
notes:
|
||||
- "Not all databases support enumeration, check system documentation for details"
|
||||
requirements: [ ]
|
||||
author: Brian Coca
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# get root user info
|
||||
- getent: database=passwd key=root
|
||||
register: root_info
|
||||
|
||||
# get all groups
|
||||
- getent: database=group split=':'
|
||||
register: groups
|
||||
|
||||
# get all hosts, split by tab
|
||||
- getent: database=hosts
|
||||
register: hosts
|
||||
|
||||
# get http service info, no error if missing
|
||||
- getent: database=services key=http fail_key=False
|
||||
register: http_info
|
||||
|
||||
# get user password hash (requires sudo/root)
|
||||
- getent: database=shadow key=www-data split=:
|
||||
register: pw_hash
|
||||
|
||||
'''
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
database = dict(required=True),
|
||||
key = dict(required=False, default=None),
|
||||
split = dict(required=False, default=None),
|
||||
fail_key = dict(required=False, default=True),
|
||||
),
|
||||
supports_check_mode = True,
|
||||
)
|
||||
|
||||
colon = [ 'passwd', 'shadow', 'group', 'gshadow' ]
|
||||
|
||||
database = module.params['database']
|
||||
key = module.params.get('key')
|
||||
split = module.params.get('split')
|
||||
fail_key = module.params.get('fail_key')
|
||||
|
||||
getent_bin = module.get_bin_path('getent', True)
|
||||
|
||||
if key is not None:
|
||||
cmd = [ getent_bin, database, key ]
|
||||
else:
|
||||
cmd = [ getent_bin, database ]
|
||||
|
||||
if split is None and database in colon:
|
||||
split = ':'
|
||||
|
||||
try:
|
||||
rc, out, err = module.run_command(cmd)
|
||||
except Exception, e:
|
||||
module.fail_json(msg=str(e))
|
||||
|
||||
msg = "Unexpected failure!"
|
||||
dbtree = 'getent_%s' % database
|
||||
results = { dbtree: {} }
|
||||
|
||||
if rc == 0:
|
||||
for line in out.splitlines():
|
||||
record = line.split(split)
|
||||
results[dbtree][record[0]] = record[1:]
|
||||
|
||||
module.exit_json(ansible_facts=results)
|
||||
|
||||
elif rc == 1:
|
||||
msg = "Missing arguments, or database unknown."
|
||||
elif rc == 2:
|
||||
msg = "One or more supplied key could not be found in the database."
|
||||
if not fail_key:
|
||||
results[dbtree][key] = None
|
||||
module.exit_json(ansible_facts=results, msg=msg)
|
||||
elif rc == 3:
|
||||
msg = "Enumeration not supported on this database."
|
||||
|
||||
module.fail_json(msg=msg)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
||||
|
||||
141
lib/ansible/modules/extras/system/kernel_blacklist
Normal file
141
lib/ansible/modules/extras/system/kernel_blacklist
Normal file
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Matthias Vogelgesang <matthias.vogelgesang@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: kernel_blacklist
|
||||
author: Matthias Vogelgesang
|
||||
version_added: 1.4
|
||||
short_description: Blacklist kernel modules
|
||||
description:
|
||||
- Add or remove kernel modules from blacklist.
|
||||
options:
|
||||
name:
|
||||
required: true
|
||||
description:
|
||||
- Name of kernel module to black- or whitelist.
|
||||
state:
|
||||
required: false
|
||||
default: "present"
|
||||
choices: [ present, absent ]
|
||||
description:
|
||||
- Whether the module should be present in the blacklist or absent.
|
||||
blacklist_file:
|
||||
required: false
|
||||
description:
|
||||
- If specified, use this blacklist file instead of
|
||||
C(/etc/modprobe.d/blacklist-ansible.conf).
|
||||
default: null
|
||||
requirements: []
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Blacklist the nouveau driver module
|
||||
- kernel_blacklist: name=nouveau state=present
|
||||
'''
|
||||
|
||||
|
||||
class Blacklist(object):
|
||||
def __init__(self, module, filename):
|
||||
if not os.path.exists(filename):
|
||||
open(filename, 'a').close()
|
||||
|
||||
self.filename = filename
|
||||
self.module = module
|
||||
|
||||
def get_pattern(self):
|
||||
return '^blacklist\s*' + self.module + '$'
|
||||
|
||||
def readlines(self):
|
||||
f = open(self.filename, 'r')
|
||||
lines = f.readlines()
|
||||
f.close()
|
||||
return lines
|
||||
|
||||
def module_listed(self):
|
||||
lines = self.readlines()
|
||||
pattern = self.get_pattern()
|
||||
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
if stripped.startswith('#'):
|
||||
continue
|
||||
|
||||
if re.match(pattern, stripped):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def remove_module(self):
|
||||
lines = self.readlines()
|
||||
pattern = self.get_pattern()
|
||||
|
||||
f = open(self.filename, 'w')
|
||||
|
||||
for line in lines:
|
||||
if not re.match(pattern, line.strip()):
|
||||
f.write(line)
|
||||
|
||||
f.close()
|
||||
|
||||
def add_module(self):
|
||||
f = open(self.filename, 'a')
|
||||
f.write('blacklist %s\n' % self.module)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(required=True),
|
||||
state=dict(required=False, choices=['present', 'absent'],
|
||||
default='present'),
|
||||
blacklist_file=dict(required=False, default=None)
|
||||
),
|
||||
supports_check_mode=False,
|
||||
)
|
||||
|
||||
args = dict(changed=False, failed=False,
|
||||
name=module.params['name'], state=module.params['state'])
|
||||
|
||||
filename = '/etc/modprobe.d/blacklist-ansible.conf'
|
||||
|
||||
if module.params['blacklist_file']:
|
||||
filename = module.params['blacklist_file']
|
||||
|
||||
blacklist = Blacklist(args['name'], filename)
|
||||
|
||||
if blacklist.module_listed():
|
||||
if args['state'] == 'absent':
|
||||
blacklist.remove_module()
|
||||
args['changed'] = True
|
||||
else:
|
||||
if args['state'] == 'present':
|
||||
blacklist.add_module()
|
||||
args['changed'] = True
|
||||
|
||||
module.exit_json(**args)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
||||
151
lib/ansible/modules/extras/system/locale_gen
Normal file
151
lib/ansible/modules/extras/system/locale_gen
Normal file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import os.path
|
||||
from subprocess import Popen, PIPE, call
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: locale_gen
|
||||
short_description: Creates of removes locales.
|
||||
description:
|
||||
- Manages locales by editing /etc/locale.gen and invoking locale-gen.
|
||||
version_added: "1.6"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name and encoding of the locale, such as "en_GB.UTF-8".
|
||||
required: true
|
||||
default: null
|
||||
aliases: []
|
||||
state:
|
||||
description:
|
||||
- Whether the locale shall be present.
|
||||
required: false
|
||||
choices: ["present", "absent"]
|
||||
default: "present"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Ensure a locale exists.
|
||||
- locale_gen: name=de_CH.UTF-8 state=present
|
||||
'''
|
||||
|
||||
# ===========================================
|
||||
# location module specific support methods.
|
||||
#
|
||||
|
||||
def is_present(name):
|
||||
"""Checks if the given locale is currently installed."""
|
||||
output = Popen(["locale", "-a"], stdout=PIPE).communicate()[0]
|
||||
return any(fix_case(name) == fix_case(line) for line in output.splitlines())
|
||||
|
||||
def fix_case(name):
|
||||
"""locale -a might return the encoding in either lower or upper case.
|
||||
Passing through this function makes them uniform for comparisons."""
|
||||
return name.replace(".utf8", ".UTF-8")
|
||||
|
||||
def replace_line(existing_line, new_line):
|
||||
"""Replaces lines in /etc/locale.gen"""
|
||||
with open("/etc/locale.gen", "r") as f:
|
||||
lines = [line.replace(existing_line, new_line) for line in f]
|
||||
with open("/etc/locale.gen", "w") as f:
|
||||
f.write("".join(lines))
|
||||
|
||||
def apply_change(targetState, name, encoding):
|
||||
"""Create or remove locale.
|
||||
|
||||
Keyword arguments:
|
||||
targetState -- Desired state, either present or absent.
|
||||
name -- Name including encoding such as de_CH.UTF-8.
|
||||
encoding -- Encoding such as UTF-8.
|
||||
"""
|
||||
if targetState=="present":
|
||||
# Create locale.
|
||||
replace_line("# "+name+" "+encoding, name+" "+encoding)
|
||||
else:
|
||||
# Delete locale.
|
||||
replace_line(name+" "+encoding, "# "+name+" "+encoding)
|
||||
|
||||
localeGenExitValue = call("locale-gen")
|
||||
if localeGenExitValue!=0:
|
||||
raise EnvironmentError(localeGenExitValue, "locale.gen failed to execute, it returned "+str(localeGenExitValue))
|
||||
|
||||
def apply_change_ubuntu(targetState, name, encoding):
|
||||
"""Create or remove locale.
|
||||
|
||||
Keyword arguments:
|
||||
targetState -- Desired state, either present or absent.
|
||||
name -- Name including encoding such as de_CH.UTF-8.
|
||||
encoding -- Encoding such as UTF-8.
|
||||
"""
|
||||
if targetState=="present":
|
||||
# Create locale.
|
||||
# Ubuntu's patched locale-gen automatically adds the new locale to /var/lib/locales/supported.d/local
|
||||
localeGenExitValue = call(["locale-gen", name])
|
||||
else:
|
||||
# Delete locale involves discarding the locale from /var/lib/locales/supported.d/local and regenerating all locales.
|
||||
with open("/var/lib/locales/supported.d/local", "r") as f:
|
||||
content = f.readlines()
|
||||
with open("/var/lib/locales/supported.d/local", "w") as f:
|
||||
for line in content:
|
||||
if line!=(name+" "+encoding+"\n"):
|
||||
f.write(line)
|
||||
# Purge locales and regenerate.
|
||||
# Please provide a patch if you know how to avoid regenerating the locales to keep!
|
||||
localeGenExitValue = call(["locale-gen", "--purge"])
|
||||
|
||||
if localeGenExitValue!=0:
|
||||
raise EnvironmentError(localeGenExitValue, "locale.gen failed to execute, it returned "+str(localeGenExitValue))
|
||||
|
||||
# ==============================================================
|
||||
# main
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
name = dict(required=True),
|
||||
state = dict(choices=['present','absent'], required=True),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
name = module.params['name']
|
||||
if not "." in name:
|
||||
module.fail_json(msg="Locale does not match pattern. Did you specify the encoding?")
|
||||
state = module.params['state']
|
||||
|
||||
if not os.path.exists("/etc/locale.gen"):
|
||||
if os.path.exists("/var/lib/locales/supported.d/local"):
|
||||
# Ubuntu created its own system to manage locales.
|
||||
ubuntuMode = True
|
||||
else:
|
||||
module.fail_json(msg="/etc/locale.gen and /var/lib/locales/supported.d/local are missing. Is the package “locales” installed?")
|
||||
else:
|
||||
# We found the common way to manage locales.
|
||||
ubuntuMode = False
|
||||
|
||||
prev_state = "present" if is_present(name) else "absent"
|
||||
changed = (prev_state!=state)
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=changed)
|
||||
else:
|
||||
encoding = name.split(".")[1]
|
||||
if changed:
|
||||
try:
|
||||
if ubuntuMode==False:
|
||||
apply_change(state, name, encoding)
|
||||
else:
|
||||
apply_change_ubuntu(state, name, encoding)
|
||||
except EnvironmentError as e:
|
||||
module.fail_json(msg=e.strerror, exitValue=e.errno)
|
||||
|
||||
module.exit_json(name=name, changed=changed, msg="OK")
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
||||
253
lib/ansible/modules/extras/system/lvg
Normal file
253
lib/ansible/modules/extras/system/lvg
Normal file
@@ -0,0 +1,253 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Alexander Bulimov <lazywolf0@gmail.com>
|
||||
# based on lvol module by Jeroen Hoekx <jeroen.hoekx@dsquare.be>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
author: Alexander Bulimov
|
||||
module: lvg
|
||||
short_description: Configure LVM volume groups
|
||||
description:
|
||||
- This module creates, removes or resizes volume groups.
|
||||
version_added: "1.1"
|
||||
options:
|
||||
vg:
|
||||
description:
|
||||
- The name of the volume group.
|
||||
required: true
|
||||
pvs:
|
||||
description:
|
||||
- List of comma-separated devices to use as physical devices in this volume group. Required when creating or resizing volume group.
|
||||
required: false
|
||||
pesize:
|
||||
description:
|
||||
- The size of the physical extent in megabytes. Must be a power of 2.
|
||||
default: 4
|
||||
required: false
|
||||
vg_options:
|
||||
description:
|
||||
- Additional options to pass to C(vgcreate) when creating the volume group.
|
||||
default: null
|
||||
required: false
|
||||
version_added: "1.6"
|
||||
state:
|
||||
choices: [ "present", "absent" ]
|
||||
default: present
|
||||
description:
|
||||
- Control if the volume group exists.
|
||||
required: false
|
||||
force:
|
||||
choices: [ "yes", "no" ]
|
||||
default: "no"
|
||||
description:
|
||||
- If yes, allows to remove volume group with logical volumes.
|
||||
required: false
|
||||
notes:
|
||||
- module does not modify PE size for already present volume group
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Create a volume group on top of /dev/sda1 with physical extent size = 32MB.
|
||||
- lvg: vg=vg.services pvs=/dev/sda1 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,
|
||||
# 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,
|
||||
# and then reduce by /dev/sda5.
|
||||
- lvg: vg=vg.services pvs=/dev/sdb1,/dev/sdc5
|
||||
|
||||
# Remove a volume group with name vg.services.
|
||||
- lvg: vg=vg.services state=absent
|
||||
'''
|
||||
|
||||
def parse_vgs(data):
|
||||
vgs = []
|
||||
for line in data.splitlines():
|
||||
parts = line.strip().split(';')
|
||||
vgs.append({
|
||||
'name': parts[0],
|
||||
'pv_count': int(parts[1]),
|
||||
'lv_count': int(parts[2]),
|
||||
})
|
||||
return vgs
|
||||
|
||||
def find_mapper_device_name(module, dm_device):
|
||||
dmsetup_cmd = module.get_bin_path('dmsetup', True)
|
||||
mapper_prefix = '/dev/mapper/'
|
||||
rc, dm_name, err = module.run_command("%s info -C --noheadings -o name %s" % (dmsetup_cmd, dm_device))
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Failed executing dmsetup command.", rc=rc, err=err)
|
||||
mapper_device = mapper_prefix + dm_name.rstrip()
|
||||
return mapper_device
|
||||
|
||||
def parse_pvs(module, data):
|
||||
pvs = []
|
||||
dm_prefix = '/dev/dm-'
|
||||
for line in data.splitlines():
|
||||
parts = line.strip().split(';')
|
||||
if parts[0].startswith(dm_prefix):
|
||||
parts[0] = find_mapper_device_name(module, parts[0])
|
||||
pvs.append({
|
||||
'name': parts[0],
|
||||
'vg_name': parts[1],
|
||||
})
|
||||
return pvs
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
vg=dict(required=True),
|
||||
pvs=dict(type='list'),
|
||||
pesize=dict(type='int', default=4),
|
||||
vg_options=dict(default=''),
|
||||
state=dict(choices=["absent", "present"], default='present'),
|
||||
force=dict(type='bool', default='no'),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
vg = module.params['vg']
|
||||
state = module.params['state']
|
||||
force = module.boolean(module.params['force'])
|
||||
pesize = module.params['pesize']
|
||||
vgoptions = module.params['vg_options'].split()
|
||||
|
||||
if module.params['pvs']:
|
||||
dev_string = ' '.join(module.params['pvs'])
|
||||
dev_list = module.params['pvs']
|
||||
elif state == 'present':
|
||||
module.fail_json(msg="No physical volumes given.")
|
||||
|
||||
|
||||
|
||||
if state=='present':
|
||||
### check given devices
|
||||
for test_dev in dev_list:
|
||||
if not os.path.exists(test_dev):
|
||||
module.fail_json(msg="Device %s not found."%test_dev)
|
||||
|
||||
### get pv list
|
||||
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)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Failed executing pvs command.",rc=rc, err=err)
|
||||
|
||||
### check pv for devices
|
||||
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 ]
|
||||
if used_pvs:
|
||||
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)
|
||||
rc,current_vgs,err = module.run_command("%s --noheadings -o vg_name,pv_count,lv_count --separator ';'" % vgs_cmd)
|
||||
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Failed executing vgs command.",rc=rc, err=err)
|
||||
|
||||
changed = False
|
||||
|
||||
vgs = parse_vgs(current_vgs)
|
||||
|
||||
for test_vg in vgs:
|
||||
if test_vg['name'] == vg:
|
||||
this_vg = test_vg
|
||||
break
|
||||
else:
|
||||
this_vg = None
|
||||
|
||||
if this_vg is None:
|
||||
if state == 'present':
|
||||
### create VG
|
||||
if module.check_mode:
|
||||
changed = True
|
||||
else:
|
||||
### create PV
|
||||
pvcreate_cmd = module.get_bin_path('pvcreate', True)
|
||||
for current_dev in dev_list:
|
||||
rc,_,err = module.run_command("%s %s" % (pvcreate_cmd,current_dev))
|
||||
if rc == 0:
|
||||
changed = True
|
||||
else:
|
||||
module.fail_json(msg="Creating physical volume '%s' failed" % current_dev, rc=rc, err=err)
|
||||
vgcreate_cmd = module.get_bin_path('vgcreate')
|
||||
rc,_,err = module.run_command([vgcreate_cmd] + vgoptions + ['-s', str(pesize), vg, dev_string])
|
||||
if rc == 0:
|
||||
changed = True
|
||||
else:
|
||||
module.fail_json(msg="Creating volume group '%s' failed"%vg, rc=rc, err=err)
|
||||
else:
|
||||
if state == 'absent':
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
else:
|
||||
if this_vg['lv_count'] == 0 or force:
|
||||
### remove VG
|
||||
vgremove_cmd = module.get_bin_path('vgremove', True)
|
||||
rc,_,err = module.run_command("%s --force %s" % (vgremove_cmd, vg))
|
||||
if rc == 0:
|
||||
module.exit_json(changed=True)
|
||||
else:
|
||||
module.fail_json(msg="Failed to remove volume group %s"%(vg),rc=rc, err=err)
|
||||
else:
|
||||
module.fail_json(msg="Refuse to remove non-empty volume group %s without force=yes"%(vg))
|
||||
|
||||
### resize VG
|
||||
current_devs = [ pv['name'] for pv in pvs if pv['vg_name'] == vg ]
|
||||
devs_to_remove = list(set(current_devs) - set(dev_list))
|
||||
devs_to_add = list(set(dev_list) - set(current_devs))
|
||||
|
||||
if devs_to_add or devs_to_remove:
|
||||
if module.check_mode:
|
||||
changed = True
|
||||
else:
|
||||
if devs_to_add:
|
||||
devs_to_add_string = ' '.join(devs_to_add)
|
||||
### create PV
|
||||
pvcreate_cmd = module.get_bin_path('pvcreate', True)
|
||||
for current_dev in devs_to_add:
|
||||
rc,_,err = module.run_command("%s %s" % (pvcreate_cmd, current_dev))
|
||||
if rc == 0:
|
||||
changed = True
|
||||
else:
|
||||
module.fail_json(msg="Creating physical volume '%s' failed"%current_dev, rc=rc, err=err)
|
||||
### add PV to our VG
|
||||
vgextend_cmd = module.get_bin_path('vgextend', True)
|
||||
rc,_,err = module.run_command("%s %s %s" % (vgextend_cmd, vg, devs_to_add_string))
|
||||
if rc == 0:
|
||||
changed = True
|
||||
else:
|
||||
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
|
||||
if devs_to_remove:
|
||||
devs_to_remove_string = ' '.join(devs_to_remove)
|
||||
vgreduce_cmd = module.get_bin_path('vgreduce', True)
|
||||
rc,_,err = module.run_command("%s --force %s %s" % (vgreduce_cmd, vg, devs_to_remove_string))
|
||||
if rc == 0:
|
||||
changed = True
|
||||
else:
|
||||
module.fail_json(msg="Unable to reduce %s by %s."%(vg, devs_to_remove_string),rc=rc,err=err)
|
||||
|
||||
module.exit_json(changed=changed)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
||||
235
lib/ansible/modules/extras/system/lvol
Normal file
235
lib/ansible/modules/extras/system/lvol
Normal file
@@ -0,0 +1,235 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Jeroen Hoekx <jeroen.hoekx@dsquare.be>, Alexander Bulimov <lazywolf0@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
author: Jeroen Hoekx
|
||||
module: lvol
|
||||
short_description: Configure LVM logical volumes
|
||||
description:
|
||||
- This module creates, removes or resizes logical volumes.
|
||||
version_added: "1.1"
|
||||
options:
|
||||
vg:
|
||||
description:
|
||||
- The volume group this logical volume is part of.
|
||||
required: true
|
||||
lv:
|
||||
description:
|
||||
- The name of the logical volume.
|
||||
required: true
|
||||
size:
|
||||
description:
|
||||
- The size of the logical volume, according to lvcreate(8) --size, by
|
||||
default in megabytes or optionally with one of [bBsSkKmMgGtTpPeE] units; or
|
||||
according to lvcreate(8) --extents as a percentage of [VG|PVS|FREE];
|
||||
resizing is not supported with percentages.
|
||||
state:
|
||||
choices: [ "present", "absent" ]
|
||||
default: present
|
||||
description:
|
||||
- Control if the logical volume exists.
|
||||
required: false
|
||||
force:
|
||||
version_added: "1.5"
|
||||
choices: [ "yes", "no" ]
|
||||
default: "no"
|
||||
description:
|
||||
- Shrink or remove operations of volumes requires this switch. Ensures that
|
||||
that filesystems get never corrupted/destroyed by mistake.
|
||||
required: false
|
||||
notes:
|
||||
- Filesystems on top of the volume are not resized.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Create a logical volume of 512m.
|
||||
- lvol: vg=firefly lv=test size=512
|
||||
|
||||
# Create a logical volume of 512g.
|
||||
- lvol: vg=firefly lv=test size=512g
|
||||
|
||||
# Create a logical volume the size of all remaining space in the volume group
|
||||
- lvol: vg=firefly lv=test size=100%FREE
|
||||
|
||||
# Extend the logical volume to 1024m.
|
||||
- lvol: vg=firefly lv=test size=1024
|
||||
|
||||
# Reduce the logical volume to 512m
|
||||
- lvol: vg=firefly lv=test size=512 force=yes
|
||||
|
||||
# Remove the logical volume.
|
||||
- lvol: vg=firefly lv=test state=absent force=yes
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
decimal_point = re.compile(r"(\.|,)")
|
||||
|
||||
|
||||
def parse_lvs(data):
|
||||
lvs = []
|
||||
for line in data.splitlines():
|
||||
parts = line.strip().split(';')
|
||||
lvs.append({
|
||||
'name': parts[0],
|
||||
'size': int(decimal_point.split(parts[1])[0]),
|
||||
})
|
||||
return lvs
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
vg=dict(required=True),
|
||||
lv=dict(required=True),
|
||||
size=dict(),
|
||||
state=dict(choices=["absent", "present"], default='present'),
|
||||
force=dict(type='bool', default='no'),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
vg = module.params['vg']
|
||||
lv = module.params['lv']
|
||||
size = module.params['size']
|
||||
state = module.params['state']
|
||||
force = module.boolean(module.params['force'])
|
||||
size_opt = 'L'
|
||||
size_unit = 'm'
|
||||
|
||||
if size:
|
||||
# LVCREATE(8) -l --extents option with percentage
|
||||
if '%' in size:
|
||||
size_parts = size.split('%', 1)
|
||||
size_percent = int(size_parts[0])
|
||||
if size_percent > 100:
|
||||
module.fail_json(msg="Size percentage cannot be larger than 100%")
|
||||
size_whole = size_parts[1]
|
||||
if size_whole == 'ORIGIN':
|
||||
module.fail_json(msg="Snapshot Volumes are not supported")
|
||||
elif size_whole not in ['VG', 'PVS', 'FREE']:
|
||||
module.fail_json(msg="Specify extents as a percentage of VG|PVS|FREE")
|
||||
size_opt = 'l'
|
||||
size_unit = ''
|
||||
|
||||
# LVCREATE(8) -L --size option unit
|
||||
elif size[-1].isalpha():
|
||||
if size[-1] in 'bBsSkKmMgGtTpPeE':
|
||||
size_unit = size[-1]
|
||||
if size[0:-1].isdigit():
|
||||
size = int(size[0:-1])
|
||||
else:
|
||||
module.fail_json(msg="Bad size specification for unit %s" % size_unit)
|
||||
size_opt = 'L'
|
||||
else:
|
||||
module.fail_json(msg="Size unit should be one of [bBsSkKmMgGtTpPeE]")
|
||||
# when no unit, megabytes by default
|
||||
elif size.isdigit():
|
||||
size = int(size)
|
||||
else:
|
||||
module.fail_json(msg="Bad size specification")
|
||||
|
||||
if size_opt == 'l':
|
||||
unit = 'm'
|
||||
else:
|
||||
unit = size_unit
|
||||
|
||||
rc, current_lvs, err = module.run_command(
|
||||
"lvs --noheadings -o lv_name,size --units %s --separator ';' %s" % (unit, vg))
|
||||
|
||||
if rc != 0:
|
||||
if state == 'absent':
|
||||
module.exit_json(changed=False, stdout="Volume group %s does not exist." % vg, stderr=False)
|
||||
else:
|
||||
module.fail_json(msg="Volume group %s does not exist." % vg, rc=rc, err=err)
|
||||
|
||||
changed = False
|
||||
|
||||
lvs = parse_lvs(current_lvs)
|
||||
|
||||
for test_lv in lvs:
|
||||
if test_lv['name'] == lv:
|
||||
this_lv = test_lv
|
||||
break
|
||||
else:
|
||||
this_lv = None
|
||||
|
||||
if state == 'present' and not size:
|
||||
if this_lv is None:
|
||||
module.fail_json(msg="No size given.")
|
||||
else:
|
||||
module.exit_json(changed=False, vg=vg, lv=this_lv['name'], size=this_lv['size'])
|
||||
|
||||
msg = ''
|
||||
if this_lv is None:
|
||||
if state == 'present':
|
||||
### create LV
|
||||
if module.check_mode:
|
||||
changed = True
|
||||
else:
|
||||
rc, _, err = module.run_command("lvcreate -n %s -%s %s%s %s" % (lv, size_opt, size, size_unit, vg))
|
||||
if rc == 0:
|
||||
changed = True
|
||||
else:
|
||||
module.fail_json(msg="Creating logical volume '%s' failed" % lv, rc=rc, err=err)
|
||||
else:
|
||||
if state == 'absent':
|
||||
### remove LV
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
if not force:
|
||||
module.fail_json(msg="Sorry, no removal of logical volume %s without force=yes." % (this_lv['name']))
|
||||
rc, _, err = module.run_command("lvremove --force %s/%s" % (vg, this_lv['name']))
|
||||
if rc == 0:
|
||||
module.exit_json(changed=True)
|
||||
else:
|
||||
module.fail_json(msg="Failed to remove logical volume %s" % (lv), rc=rc, err=err)
|
||||
|
||||
elif size_opt == 'l':
|
||||
module.exit_json(changed=False, msg="Resizing extents with percentage not supported.")
|
||||
else:
|
||||
### resize LV
|
||||
tool = None
|
||||
if size > this_lv['size']:
|
||||
tool = 'lvextend'
|
||||
elif size < this_lv['size']:
|
||||
if not force:
|
||||
module.fail_json(msg="Sorry, no shrinking of %s without force=yes." % (this_lv['name']))
|
||||
tool = 'lvreduce --force'
|
||||
|
||||
if tool:
|
||||
if module.check_mode:
|
||||
changed = True
|
||||
else:
|
||||
rc, _, err = module.run_command("%s -%s %s%s %s/%s" % (tool, size_opt, size, size_unit, vg, this_lv['name']))
|
||||
if rc == 0:
|
||||
changed = True
|
||||
elif "matches existing size" in err:
|
||||
module.exit_json(changed=False, vg=vg, lv=this_lv['name'], size=this_lv['size'])
|
||||
else:
|
||||
module.fail_json(msg="Unable to resize %s to %s%s" % (lv, size, size_unit), rc=rc, err=err)
|
||||
|
||||
module.exit_json(changed=changed, msg=msg)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
||||
115
lib/ansible/modules/extras/system/modprobe
Normal file
115
lib/ansible/modules/extras/system/modprobe
Normal file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, David Stygstra <david.stygstra@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# This module is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This software is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: modprobe
|
||||
short_description: Add or remove kernel modules
|
||||
requirements: []
|
||||
version_added: 1.4
|
||||
author: David Stygstra, Julien Dauphant, Matt Jeffery
|
||||
description:
|
||||
- Add or remove kernel modules.
|
||||
options:
|
||||
name:
|
||||
required: true
|
||||
description:
|
||||
- Name of kernel module to manage.
|
||||
state:
|
||||
required: false
|
||||
default: "present"
|
||||
choices: [ present, absent ]
|
||||
description:
|
||||
- Whether the module should be present or absent.
|
||||
params:
|
||||
required: false
|
||||
default: ""
|
||||
version_added: "1.6"
|
||||
description:
|
||||
- Modules parameters.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Add the 802.1q module
|
||||
- modprobe: name=8021q state=present
|
||||
# Add the dummy module
|
||||
- modprobe: name=dummy state=present params="numdummies=2"
|
||||
'''
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec={
|
||||
'name': {'required': True},
|
||||
'state': {'default': 'present', 'choices': ['present', 'absent']},
|
||||
'params': {'default': ''},
|
||||
},
|
||||
supports_check_mode=True,
|
||||
)
|
||||
args = {
|
||||
'changed': False,
|
||||
'failed': False,
|
||||
'name': module.params['name'],
|
||||
'state': module.params['state'],
|
||||
'params': module.params['params'],
|
||||
}
|
||||
|
||||
# Check if module is present
|
||||
try:
|
||||
modules = open('/proc/modules')
|
||||
present = False
|
||||
module_name = args['name'].replace('-', '_') + ' '
|
||||
for line in modules:
|
||||
if line.startswith(module_name):
|
||||
present = True
|
||||
break
|
||||
modules.close()
|
||||
except IOError, e:
|
||||
module.fail_json(msg=str(e), **args)
|
||||
|
||||
# Check only; don't modify
|
||||
if module.check_mode:
|
||||
if args['state'] == 'present' and not present:
|
||||
changed = True
|
||||
elif args['state'] == 'absent' and present:
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
module.exit_json(changed=changed)
|
||||
|
||||
# Add/remove module as needed
|
||||
if args['state'] == 'present':
|
||||
if not present:
|
||||
rc, _, err = module.run_command(['modprobe', args['name'], args['params']])
|
||||
if rc != 0:
|
||||
module.fail_json(msg=err, **args)
|
||||
args['changed'] = True
|
||||
elif args['state'] == 'absent':
|
||||
if present:
|
||||
rc, _, err = module.run_command(['rmmod', args['name']])
|
||||
if rc != 0:
|
||||
module.fail_json(msg=err, **args)
|
||||
args['changed'] = True
|
||||
|
||||
module.exit_json(**args)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
||||
56
lib/ansible/modules/extras/system/ohai
Normal file
56
lib/ansible/modules/extras/system/ohai
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ohai
|
||||
short_description: Returns inventory data from I(Ohai)
|
||||
description:
|
||||
- Similar to the M(facter) module, this runs the I(Ohai) discovery program
|
||||
(U(http://wiki.opscode.com/display/chef/Ohai)) on the remote host and
|
||||
returns JSON inventory data.
|
||||
I(Ohai) data is a bit more verbose and nested than I(facter).
|
||||
version_added: "0.6"
|
||||
options: {}
|
||||
notes: []
|
||||
requirements: [ "ohai" ]
|
||||
author: Michael DeHaan
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Retrieve (ohai) data from all Web servers and store in one-file per host
|
||||
ansible webservers -m ohai --tree=/tmp/ohaidata
|
||||
'''
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict()
|
||||
)
|
||||
cmd = ["/usr/bin/env", "ohai"]
|
||||
rc, out, err = module.run_command(cmd, check_rc=True)
|
||||
module.exit_json(**json.loads(out))
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
||||
|
||||
|
||||
379
lib/ansible/modules/extras/system/open_iscsi
Normal file
379
lib/ansible/modules/extras/system/open_iscsi
Normal file
@@ -0,0 +1,379 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Serge van Ginderachter <serge@vanginderachter.be>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: open_iscsi
|
||||
author: Serge van Ginderachter
|
||||
version_added: "1.4"
|
||||
short_description: Manage iscsi targets with open-iscsi
|
||||
description:
|
||||
- Discover targets on given portal, (dis)connect targets, mark targets to
|
||||
manually or auto start, return device nodes of connected targets.
|
||||
requirements:
|
||||
- open_iscsi library and tools (iscsiadm)
|
||||
options:
|
||||
portal:
|
||||
required: false
|
||||
aliases: [ip]
|
||||
description:
|
||||
- the ip address of the iscsi target
|
||||
port:
|
||||
required: false
|
||||
default: 3260
|
||||
description:
|
||||
- the port on which the iscsi target process listens
|
||||
target:
|
||||
required: false
|
||||
aliases: [name, targetname]
|
||||
description:
|
||||
- the iscsi target name
|
||||
login:
|
||||
required: false
|
||||
choices: [true, false]
|
||||
description:
|
||||
- whether the target node should be connected
|
||||
node_auth:
|
||||
required: false
|
||||
default: CHAP
|
||||
description:
|
||||
- discovery.sendtargets.auth.authmethod
|
||||
node_user:
|
||||
required: false
|
||||
description:
|
||||
- discovery.sendtargets.auth.username
|
||||
node_pass:
|
||||
required: false
|
||||
description:
|
||||
- discovery.sendtargets.auth.password
|
||||
auto_node_startup:
|
||||
aliases: [automatic]
|
||||
required: false
|
||||
choices: [true, false]
|
||||
description:
|
||||
- whether the target node should be automatically connected at startup
|
||||
discover:
|
||||
required: false
|
||||
choices: [true, false]
|
||||
description:
|
||||
- whether the list of target nodes on the portal should be
|
||||
(re)discovered and added to the persistent iscsi database.
|
||||
Keep in mind that iscsiadm discovery resets configurtion, like node.startup
|
||||
to manual, hence combined with auto_node_startup=yes will allways return
|
||||
a changed state.
|
||||
show_nodes:
|
||||
required: false
|
||||
choices: [true, false]
|
||||
description:
|
||||
- whether the list of nodes in the persistent iscsi database should be
|
||||
returned by the module
|
||||
|
||||
examples:
|
||||
- description: perform a discovery on 10.1.2.3 and show available target
|
||||
nodes
|
||||
code: >
|
||||
open_iscsi: show_nodes=yes discover=yes portal=10.1.2.3
|
||||
- description: discover targets on portal and login to the one available
|
||||
(only works if exactly one target is exported to the initiator)
|
||||
code: >
|
||||
open_iscsi: portal={{iscsi_target}} login=yes discover=yes
|
||||
- description: connect to the named target, after updating the local
|
||||
persistent database (cache)
|
||||
code: >
|
||||
open_iscsi: login=yes target=iqn.1986-03.com.sun:02:f8c1f9e0-c3ec-ec84-c9c9-8bfb0cd5de3d
|
||||
- description: discconnect from the cached named target
|
||||
code: >
|
||||
open_iscsi: login=no target=iqn.1986-03.com.sun:02:f8c1f9e0-c3ec-ec84-c9c9-8bfb0cd5de3d"
|
||||
'''
|
||||
|
||||
import glob
|
||||
import time
|
||||
|
||||
ISCSIADM = 'iscsiadm'
|
||||
|
||||
def compare_nodelists(l1, l2):
|
||||
|
||||
l1.sort()
|
||||
l2.sort()
|
||||
return l1 == l2
|
||||
|
||||
|
||||
def iscsi_get_cached_nodes(module, portal=None):
|
||||
|
||||
cmd = '%s --mode node' % iscsiadm_cmd
|
||||
(rc, out, err) = module.run_command(cmd)
|
||||
|
||||
if rc == 0:
|
||||
lines = out.splitlines()
|
||||
nodes = []
|
||||
for line in lines:
|
||||
# line format is "ip:port,target_portal_group_tag targetname"
|
||||
parts = line.split()
|
||||
if len(parts) > 2:
|
||||
module.fail_json(msg='error parsing output', cmd=cmd)
|
||||
target = parts[1]
|
||||
parts = parts[0].split(':')
|
||||
target_portal = parts[0]
|
||||
|
||||
if portal is None or portal == target_portal:
|
||||
nodes.append(target)
|
||||
|
||||
# older versions of scsiadm don't have nice return codes
|
||||
# for newer versions see iscsiadm(8); also usr/iscsiadm.c for details
|
||||
# err can contain [N|n]o records...
|
||||
elif rc == 21 or (rc == 255 and "o records found" in err):
|
||||
nodes = []
|
||||
else:
|
||||
module.fail_json(cmd=cmd, rc=rc, msg=err)
|
||||
|
||||
return nodes
|
||||
|
||||
|
||||
def iscsi_discover(module, portal, port):
|
||||
|
||||
cmd = '%s --mode discovery --type sendtargets --portal %s:%s' % (iscsiadm_cmd, portal, port)
|
||||
(rc, out, err) = module.run_command(cmd)
|
||||
|
||||
if rc > 0:
|
||||
module.fail_json(cmd=cmd, rc=rc, msg=err)
|
||||
|
||||
|
||||
def target_loggedon(module, target):
|
||||
|
||||
cmd = '%s --mode session' % iscsiadm_cmd
|
||||
(rc, out, err) = module.run_command(cmd)
|
||||
|
||||
if rc == 0:
|
||||
return target in out
|
||||
elif rc == 21:
|
||||
return False
|
||||
else:
|
||||
module.fail_json(cmd=cmd, rc=rc, msg=err)
|
||||
|
||||
|
||||
def target_login(module, target):
|
||||
|
||||
node_auth = module.params['node_auth']
|
||||
node_user = module.params['node_user']
|
||||
node_pass = module.params['node_pass']
|
||||
|
||||
if node_user:
|
||||
params = [('node.session.auth.authmethod', node_auth),
|
||||
('node.session.auth.username', node_user),
|
||||
('node.session.auth.password', node_pass)]
|
||||
for (name, value) in params:
|
||||
cmd = '%s --mode node --targetname %s --op=update --name %s --value %s' % (iscsiadm_cmd, target, name, value)
|
||||
(rc, out, err) = module.run_command(cmd)
|
||||
if rc > 0:
|
||||
module.fail_json(cmd=cmd, rc=rc, msg=err)
|
||||
|
||||
cmd = '%s --mode node --targetname %s --login' % (iscsiadm_cmd, target)
|
||||
(rc, out, err) = module.run_command(cmd)
|
||||
|
||||
if rc > 0:
|
||||
module.fail_json(cmd=cmd, rc=rc, msg=err)
|
||||
|
||||
|
||||
def target_logout(module, target):
|
||||
|
||||
cmd = '%s --mode node --targetname %s --logout' % (iscsiadm_cmd, target)
|
||||
(rc, out, err) = module.run_command(cmd)
|
||||
|
||||
if rc > 0:
|
||||
module.fail_json(cmd=cmd, rc=rc, msg=err)
|
||||
|
||||
|
||||
def target_device_node(module, target):
|
||||
|
||||
# if anyone know a better way to find out which devicenodes get created for
|
||||
# a given target...
|
||||
|
||||
devices = glob.glob('/dev/disk/by-path/*%s*' % target)
|
||||
if len(devices) == 0:
|
||||
return None
|
||||
else:
|
||||
devdisks = []
|
||||
for dev in devices:
|
||||
# exclude partitions
|
||||
if "-part" not in dev:
|
||||
devdisk = os.path.realpath(dev)
|
||||
# only add once (multi-path?)
|
||||
if devdisk not in devdisks:
|
||||
devdisks.append(devdisk)
|
||||
return devdisks
|
||||
|
||||
|
||||
def target_isauto(module, target):
|
||||
|
||||
cmd = '%s --mode node --targetname %s' % (iscsiadm_cmd, target)
|
||||
(rc, out, err) = module.run_command(cmd)
|
||||
|
||||
if rc == 0:
|
||||
lines = out.splitlines()
|
||||
for line in lines:
|
||||
if 'node.startup' in line:
|
||||
return 'automatic' in line
|
||||
return False
|
||||
else:
|
||||
module.fail_json(cmd=cmd, rc=rc, msg=err)
|
||||
|
||||
|
||||
def target_setauto(module, target):
|
||||
|
||||
cmd = '%s --mode node --targetname %s --op=update --name node.startup --value automatic' % (iscsiadm_cmd, target)
|
||||
(rc, out, err) = module.run_command(cmd)
|
||||
|
||||
if rc > 0:
|
||||
module.fail_json(cmd=cmd, rc=rc, msg=err)
|
||||
|
||||
|
||||
def target_setmanual(module, target):
|
||||
|
||||
cmd = '%s --mode node --targetname %s --op=update --name node.startup --value manual' % (iscsiadm_cmd, target)
|
||||
(rc, out, err) = module.run_command(cmd)
|
||||
|
||||
if rc > 0:
|
||||
module.fail_json(cmd=cmd, rc=rc, msg=err)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# load ansible module object
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
|
||||
# target
|
||||
portal = dict(required=False, aliases=['ip']),
|
||||
port = dict(required=False, default=3260),
|
||||
target = dict(required=False, aliases=['name', 'targetname']),
|
||||
node_auth = dict(required=False, default='CHAP'),
|
||||
node_user = dict(required=False),
|
||||
node_pass = dict(required=False),
|
||||
|
||||
# actions
|
||||
login = dict(type='bool', aliases=['state']),
|
||||
auto_node_startup = dict(type='bool', aliases=['automatic']),
|
||||
discover = dict(type='bool', default=False),
|
||||
show_nodes = dict(type='bool', default=False)
|
||||
),
|
||||
|
||||
required_together=[['discover_user', 'discover_pass'],
|
||||
['node_user', 'node_pass']],
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
global iscsiadm_cmd
|
||||
iscsiadm_cmd = module.get_bin_path('iscsiadm', required=True)
|
||||
|
||||
# parameters
|
||||
portal = module.params['portal']
|
||||
target = module.params['target']
|
||||
port = module.params['port']
|
||||
login = module.params['login']
|
||||
automatic = module.params['auto_node_startup']
|
||||
discover = module.params['discover']
|
||||
show_nodes = module.params['show_nodes']
|
||||
|
||||
check = module.check_mode
|
||||
|
||||
cached = iscsi_get_cached_nodes(module, portal)
|
||||
|
||||
# return json dict
|
||||
result = {}
|
||||
result['changed'] = False
|
||||
|
||||
if discover:
|
||||
if portal is None:
|
||||
module.fail_json(msg = "Need to specify at least the portal (ip) to discover")
|
||||
elif check:
|
||||
nodes = cached
|
||||
else:
|
||||
iscsi_discover(module, portal, port)
|
||||
nodes = iscsi_get_cached_nodes(module, portal)
|
||||
if not compare_nodelists(cached, nodes):
|
||||
result['changed'] |= True
|
||||
result['cache_updated'] = True
|
||||
else:
|
||||
nodes = cached
|
||||
|
||||
if login is not None or automatic is not None:
|
||||
if target is None:
|
||||
if len(nodes) > 1:
|
||||
module.fail_json(msg = "Need to specify a target")
|
||||
else:
|
||||
target = nodes[0]
|
||||
else:
|
||||
# check given target is in cache
|
||||
check_target = False
|
||||
for node in nodes:
|
||||
if node == target:
|
||||
check_target = True
|
||||
break
|
||||
if not check_target:
|
||||
module.fail_json(msg = "Specified target not found")
|
||||
|
||||
if show_nodes:
|
||||
result['nodes'] = nodes
|
||||
|
||||
if login is not None:
|
||||
loggedon = target_loggedon(module,target)
|
||||
if (login and loggedon) or (not login and not loggedon):
|
||||
result['changed'] |= False
|
||||
if login:
|
||||
result['devicenodes'] = target_device_node(module,target)
|
||||
elif not check:
|
||||
if login:
|
||||
target_login(module, target)
|
||||
# give udev some time
|
||||
time.sleep(1)
|
||||
result['devicenodes'] = target_device_node(module,target)
|
||||
else:
|
||||
target_logout(module, target)
|
||||
result['changed'] |= True
|
||||
result['connection_changed'] = True
|
||||
else:
|
||||
result['changed'] |= True
|
||||
result['connection_changed'] = True
|
||||
|
||||
if automatic is not None:
|
||||
isauto = target_isauto(module, target)
|
||||
if (automatic and isauto) or (not automatic and not isauto):
|
||||
result['changed'] |= False
|
||||
result['automatic_changed'] = False
|
||||
elif not check:
|
||||
if automatic:
|
||||
target_setauto(module, target)
|
||||
else:
|
||||
target_setmanual(module, target)
|
||||
result['changed'] |= True
|
||||
result['automatic_changed'] = True
|
||||
else:
|
||||
result['changed'] |= True
|
||||
result['automatic_changed'] = True
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
||||
|
||||
269
lib/ansible/modules/extras/system/ufw
Normal file
269
lib/ansible/modules/extras/system/ufw
Normal file
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2014, Ahti Kitsik <ak@ahtik.com>
|
||||
# (c) 2014, Jarno Keskikangas <jarno.keskikangas@gmail.com>
|
||||
# (c) 2013, Aleksey Ovcharenko <aleksey.ovcharenko@gmail.com>
|
||||
# (c) 2013, James Martin <jmartin@basho.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ufw
|
||||
short_description: Manage firewall with UFW
|
||||
description:
|
||||
- Manage firewall with UFW.
|
||||
version_added: 1.6
|
||||
author: Aleksey Ovcharenko, Jarno Keskikangas, Ahti Kitsik
|
||||
notes:
|
||||
- See C(man ufw) for more examples.
|
||||
requirements:
|
||||
- C(ufw) package
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- C(enabled) reloads firewall and enables firewall on boot.
|
||||
- C(disabled) unloads firewall and disables firewall on boot.
|
||||
- C(reloaded) reloads firewall.
|
||||
- C(reset) disables and resets firewall to installation defaults.
|
||||
required: false
|
||||
choices: ['enabled', 'disabled', 'reloaded', 'reset']
|
||||
policy:
|
||||
description:
|
||||
- Change the default policy for incoming or outgoing traffic.
|
||||
required: false
|
||||
alias: default
|
||||
choices: ['allow', 'deny', 'reject']
|
||||
direction:
|
||||
description:
|
||||
- Select direction for a rule or default policy command.
|
||||
required: false
|
||||
choices: ['in', 'out', 'incoming', 'outgoing']
|
||||
logging:
|
||||
description:
|
||||
- Toggles logging. Logged packets use the LOG_KERN syslog facility.
|
||||
choices: ['on', 'off', 'low', 'medium', 'high', 'full']
|
||||
required: false
|
||||
insert:
|
||||
description:
|
||||
- Insert the corresponding rule as rule number NUM
|
||||
required: false
|
||||
rule:
|
||||
description:
|
||||
- Add firewall rule
|
||||
required: false
|
||||
choices: ['allow', 'deny', 'reject', 'limit']
|
||||
log:
|
||||
description:
|
||||
- Log new connections matched to this rule
|
||||
required: false
|
||||
choices: ['yes', 'no']
|
||||
from_ip:
|
||||
description:
|
||||
- Source IP address.
|
||||
required: false
|
||||
aliases: ['from', 'src']
|
||||
default: 'any'
|
||||
from_port:
|
||||
description:
|
||||
- Source port.
|
||||
required: false
|
||||
to_ip:
|
||||
description:
|
||||
- Destination IP address.
|
||||
required: false
|
||||
aliases: ['to', 'dest']
|
||||
default: 'any'
|
||||
to_port:
|
||||
description:
|
||||
- Destination port.
|
||||
required: false
|
||||
aliases: ['port']
|
||||
proto:
|
||||
description:
|
||||
- TCP/IP protocol.
|
||||
choices: ['any', 'tcp', 'udp', 'ipv6', 'esp', 'ah']
|
||||
required: false
|
||||
name:
|
||||
description:
|
||||
- Use profile located in C(/etc/ufw/applications.d)
|
||||
required: false
|
||||
aliases: ['app']
|
||||
delete:
|
||||
description:
|
||||
- Delete rule.
|
||||
required: false
|
||||
choices: ['yes', 'no']
|
||||
interface:
|
||||
description:
|
||||
- Specify interface for rule.
|
||||
required: false
|
||||
aliases: ['if']
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Allow everything and enable UFW
|
||||
ufw: state=enabled policy=allow
|
||||
|
||||
# Set logging
|
||||
ufw: logging=on
|
||||
|
||||
# Sometimes it is desirable to let the sender know when traffic is
|
||||
# being denied, rather than simply ignoring it. In these cases, use
|
||||
# reject instead of deny. In addition, log rejected connections:
|
||||
ufw: rule=reject port=auth log=yes
|
||||
|
||||
# ufw supports connection rate limiting, which is useful for protecting
|
||||
# against brute-force login attacks. ufw will deny connections if an IP
|
||||
# address has attempted to initiate 6 or more connections in the last
|
||||
# 30 seconds. See http://www.debian-administration.org/articles/187
|
||||
# for details. Typical usage is:
|
||||
ufw: rule=limit port=ssh proto=tcp
|
||||
|
||||
# Allow OpenSSH
|
||||
ufw: rule=allow name=OpenSSH
|
||||
|
||||
# Delete OpenSSH rule
|
||||
ufw: rule=allow name=OpenSSH delete=yes
|
||||
|
||||
# Deny all access to port 53:
|
||||
ufw: rule=deny port=53
|
||||
|
||||
# Allow all access to tcp port 80:
|
||||
ufw: rule=allow port=80 proto=tcp
|
||||
|
||||
# Allow all access from RFC1918 networks to this host:
|
||||
ufw: rule=allow src={{ item }}
|
||||
with_items:
|
||||
- 10.0.0.0/8
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
|
||||
# Deny access to udp port 514 from host 1.2.3.4:
|
||||
ufw: rule=deny proto=udp src=1.2.3.4 port=514
|
||||
|
||||
# Allow incoming access to eth0 from 1.2.3.5 port 5469 to 1.2.3.4 port 5469
|
||||
ufw: rule=allow interface=eth0 direction=in proto=udp src=1.2.3.5 from_port=5469 dest=1.2.3.4 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.
|
||||
ufw: rule=deny proto=tcp src=2001:db8::/32 port=25
|
||||
'''
|
||||
|
||||
from operator import itemgetter
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
state = dict(default=None, choices=['enabled', 'disabled', 'reloaded', 'reset']),
|
||||
default = dict(default=None, aliases=['policy'], choices=['allow', 'deny', 'reject']),
|
||||
logging = dict(default=None, choices=['on', 'off', 'low', 'medium', 'high', 'full']),
|
||||
direction = dict(default=None, choices=['in', 'incoming', 'out', 'outgoing']),
|
||||
delete = dict(default=False, type='bool'),
|
||||
insert = dict(default=None),
|
||||
rule = dict(default=None, choices=['allow', 'deny', 'reject', 'limit']),
|
||||
interface = dict(default=None, aliases=['if']),
|
||||
log = dict(default=False, type='bool'),
|
||||
from_ip = dict(default='any', aliases=['src', 'from']),
|
||||
from_port = dict(default=None),
|
||||
to_ip = dict(default='any', aliases=['dest', 'to']),
|
||||
to_port = dict(default=None, aliases=['port']),
|
||||
proto = dict(default=None, aliases=['protocol'], choices=['any', 'tcp', 'udp', 'ipv6', 'esp', 'ah']),
|
||||
app = dict(default=None, aliases=['name'])
|
||||
),
|
||||
supports_check_mode = True,
|
||||
mutually_exclusive = [['app', 'proto', 'logging']]
|
||||
)
|
||||
|
||||
cmds = []
|
||||
|
||||
def execute(cmd):
|
||||
cmd = ' '.join(map(itemgetter(-1), filter(itemgetter(0), cmd)))
|
||||
|
||||
cmds.append(cmd)
|
||||
(rc, out, err) = module.run_command(cmd)
|
||||
|
||||
if rc != 0:
|
||||
module.fail_json(msg=err or out)
|
||||
|
||||
params = module.params
|
||||
|
||||
# Ensure at least one of the command arguments are given
|
||||
command_keys = ['state', 'default', 'rule', 'logging']
|
||||
commands = dict((key, params[key]) for key in command_keys if params[key])
|
||||
|
||||
if len(commands) < 1:
|
||||
module.fail_json(msg="Not any of the command arguments %s given" % commands)
|
||||
|
||||
if('interface' in params and 'direction' not in params):
|
||||
module.fail_json(msg="Direction must be specified when creating a rule on an interface")
|
||||
|
||||
# Ensure ufw is available
|
||||
ufw_bin = module.get_bin_path('ufw', True)
|
||||
|
||||
# Save the pre state and rules in order to recognize changes
|
||||
(_, pre_state, _) = module.run_command(ufw_bin + ' status verbose')
|
||||
(_, pre_rules, _) = module.run_command("grep '^### tuple' /lib/ufw/user*.rules")
|
||||
|
||||
# Execute commands
|
||||
for (command, value) in commands.iteritems():
|
||||
cmd = [[ufw_bin], [module.check_mode, '--dry-run']]
|
||||
|
||||
if command == 'state':
|
||||
states = { 'enabled': 'enable', 'disabled': 'disable',
|
||||
'reloaded': 'reload', 'reset': 'reset' }
|
||||
execute(cmd + [['-f'], [states[value]]])
|
||||
|
||||
elif command == 'logging':
|
||||
execute(cmd + [[command], [value]])
|
||||
|
||||
elif command == 'default':
|
||||
execute(cmd + [[command], [value], [params['direction']]])
|
||||
|
||||
elif command == 'rule':
|
||||
# Rules are constructed according to the long format
|
||||
#
|
||||
# ufw [--dry-run] [delete] [insert NUM] allow|deny|reject|limit [in|out on INTERFACE] [log|log-all] \
|
||||
# [from ADDRESS [port PORT]] [to ADDRESS [port PORT]] \
|
||||
# [proto protocol] [app application]
|
||||
cmd.append([module.boolean(params['delete']), 'delete'])
|
||||
cmd.append([params['insert'], "insert %s" % params['insert']])
|
||||
cmd.append([value])
|
||||
cmd.append([module.boolean(params['log']), 'log'])
|
||||
|
||||
for (key, template) in [('direction', "%s" ), ('interface', "on %s" ),
|
||||
('from_ip', "from %s" ), ('from_port', "port %s" ),
|
||||
('to_ip', "to %s" ), ('to_port', "port %s" ),
|
||||
('proto', "proto %s"), ('app', "app '%s'")]:
|
||||
|
||||
value = params[key]
|
||||
cmd.append([value, template % (value)])
|
||||
|
||||
execute(cmd)
|
||||
|
||||
# Get the new state
|
||||
(_, post_state, _) = module.run_command(ufw_bin + ' status verbose')
|
||||
(_, post_rules, _) = module.run_command("grep '^### tuple' /lib/ufw/user*.rules")
|
||||
changed = (pre_state != post_state) or (pre_rules != post_rules)
|
||||
|
||||
return module.exit_json(changed=changed, commands=cmds, msg=post_state.rstrip())
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
||||
417
lib/ansible/modules/extras/system/zfs
Normal file
417
lib/ansible/modules/extras/system/zfs
Normal file
@@ -0,0 +1,417 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Johan Wiren <johan.wiren.se@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: zfs
|
||||
short_description: Manage zfs
|
||||
description:
|
||||
- Manages ZFS file systems on Solaris and FreeBSD. Can manage file systems, volumes and snapshots. See zfs(1M) for more information about the properties.
|
||||
version_added: "1.1"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- File system, snapshot or volume name e.g. C(rpool/myfs)
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- Whether to create (C(present)), or remove (C(absent)) a file system, snapshot or volume.
|
||||
required: true
|
||||
choices: [present, absent]
|
||||
aclinherit:
|
||||
description:
|
||||
- The aclinherit property.
|
||||
required: False
|
||||
choices: [discard,noallow,restricted,passthrough,passthrough-x]
|
||||
aclmode:
|
||||
description:
|
||||
- The aclmode property.
|
||||
required: False
|
||||
choices: [discard,groupmask,passthrough]
|
||||
atime:
|
||||
description:
|
||||
- The atime property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
canmount:
|
||||
description:
|
||||
- The canmount property.
|
||||
required: False
|
||||
choices: ['on','off','noauto']
|
||||
casesensitivity:
|
||||
description:
|
||||
- The casesensitivity property.
|
||||
required: False
|
||||
choices: [sensitive,insensitive,mixed]
|
||||
checksum:
|
||||
description:
|
||||
- The checksum property.
|
||||
required: False
|
||||
choices: ['on','off',fletcher2,fletcher4,sha256]
|
||||
compression:
|
||||
description:
|
||||
- The compression property.
|
||||
required: False
|
||||
choices: ['on','off',lzjb,gzip,gzip-1,gzip-2,gzip-3,gzip-4,gzip-5,gzip-6,gzip-7,gzip-8,gzip-9,lz4,zle]
|
||||
copies:
|
||||
description:
|
||||
- The copies property.
|
||||
required: False
|
||||
choices: [1,2,3]
|
||||
dedup:
|
||||
description:
|
||||
- The dedup property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
devices:
|
||||
description:
|
||||
- The devices property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
exec:
|
||||
description:
|
||||
- The exec property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
jailed:
|
||||
description:
|
||||
- The jailed property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
logbias:
|
||||
description:
|
||||
- The logbias property.
|
||||
required: False
|
||||
choices: [latency,throughput]
|
||||
mountpoint:
|
||||
description:
|
||||
- The mountpoint property.
|
||||
required: False
|
||||
nbmand:
|
||||
description:
|
||||
- The nbmand property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
normalization:
|
||||
description:
|
||||
- The normalization property.
|
||||
required: False
|
||||
choices: [none,formC,formD,formKC,formKD]
|
||||
primarycache:
|
||||
description:
|
||||
- The primarycache property.
|
||||
required: False
|
||||
choices: [all,none,metadata]
|
||||
quota:
|
||||
description:
|
||||
- The quota property.
|
||||
required: False
|
||||
readonly:
|
||||
description:
|
||||
- The readonly property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
recordsize:
|
||||
description:
|
||||
- The recordsize property.
|
||||
required: False
|
||||
refquota:
|
||||
description:
|
||||
- The refquota property.
|
||||
required: False
|
||||
refreservation:
|
||||
description:
|
||||
- The refreservation property.
|
||||
required: False
|
||||
reservation:
|
||||
description:
|
||||
- The reservation property.
|
||||
required: False
|
||||
secondarycache:
|
||||
description:
|
||||
- The secondarycache property.
|
||||
required: False
|
||||
choices: [all,none,metadata]
|
||||
setuid:
|
||||
description:
|
||||
- The setuid property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
shareiscsi:
|
||||
description:
|
||||
- The shareiscsi property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
sharenfs:
|
||||
description:
|
||||
- The sharenfs property.
|
||||
required: False
|
||||
sharesmb:
|
||||
description:
|
||||
- The sharesmb property.
|
||||
required: False
|
||||
snapdir:
|
||||
description:
|
||||
- The snapdir property.
|
||||
required: False
|
||||
choices: [hidden,visible]
|
||||
sync:
|
||||
description:
|
||||
- The sync property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
utf8only:
|
||||
description:
|
||||
- The utf8only property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
volsize:
|
||||
description:
|
||||
- The volsize property.
|
||||
required: False
|
||||
volblocksize:
|
||||
description:
|
||||
- The volblocksize property.
|
||||
required: False
|
||||
vscan:
|
||||
description:
|
||||
- The vscan property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
xattr:
|
||||
description:
|
||||
- The xattr property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
zoned:
|
||||
description:
|
||||
- The zoned property.
|
||||
required: False
|
||||
choices: ['on','off']
|
||||
author: Johan Wiren
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Create a new file system called myfs in pool rpool
|
||||
- zfs: name=rpool/myfs state=present
|
||||
|
||||
# Create a new volume called myvol in pool rpool.
|
||||
- zfs: name=rpool/myvol state=present volsize=10M
|
||||
|
||||
# Create a snapshot of rpool/myfs file system.
|
||||
- zfs: name=rpool/myfs@mysnapshot state=present
|
||||
|
||||
# Create a new file system called myfs2 with snapdir enabled
|
||||
- zfs: name=rpool/myfs2 state=present snapdir=enabled
|
||||
'''
|
||||
|
||||
|
||||
import os
|
||||
|
||||
class Zfs(object):
|
||||
def __init__(self, module, name, properties):
|
||||
self.module = module
|
||||
self.name = name
|
||||
self.properties = properties
|
||||
self.changed = False
|
||||
|
||||
self.immutable_properties = [ 'casesensitivity', 'normalization', 'utf8only' ]
|
||||
|
||||
def exists(self):
|
||||
cmd = [self.module.get_bin_path('zfs', True)]
|
||||
cmd.append('list')
|
||||
cmd.append('-t all')
|
||||
cmd.append(self.name)
|
||||
(rc, out, err) = self.module.run_command(' '.join(cmd))
|
||||
if rc == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def create(self):
|
||||
if self.module.check_mode:
|
||||
self.changed = True
|
||||
return
|
||||
properties=self.properties
|
||||
volsize = properties.pop('volsize', None)
|
||||
volblocksize = properties.pop('volblocksize', None)
|
||||
if "@" in self.name:
|
||||
action = 'snapshot'
|
||||
else:
|
||||
action = 'create'
|
||||
|
||||
cmd = [self.module.get_bin_path('zfs', True)]
|
||||
cmd.append(action)
|
||||
if volblocksize:
|
||||
cmd.append('-b %s' % volblocksize)
|
||||
if properties:
|
||||
for prop, value in properties.iteritems():
|
||||
cmd.append('-o %s="%s"' % (prop, value))
|
||||
if volsize:
|
||||
cmd.append('-V')
|
||||
cmd.append(volsize)
|
||||
cmd.append(self.name)
|
||||
(rc, err, out) = self.module.run_command(' '.join(cmd))
|
||||
if rc == 0:
|
||||
self.changed=True
|
||||
else:
|
||||
self.module.fail_json(msg=out)
|
||||
|
||||
def destroy(self):
|
||||
if self.module.check_mode:
|
||||
self.changed = True
|
||||
return
|
||||
cmd = [self.module.get_bin_path('zfs', True)]
|
||||
cmd.append('destroy')
|
||||
cmd.append(self.name)
|
||||
(rc, err, out) = self.module.run_command(' '.join(cmd))
|
||||
if rc == 0:
|
||||
self.changed = True
|
||||
else:
|
||||
self.module.fail_json(msg=out)
|
||||
|
||||
def set_property(self, prop, value):
|
||||
if self.module.check_mode:
|
||||
self.changed = True
|
||||
return
|
||||
cmd = self.module.get_bin_path('zfs', True)
|
||||
args = [cmd, 'set', prop + '=' + value, self.name]
|
||||
(rc, err, out) = self.module.run_command(args)
|
||||
if rc == 0:
|
||||
self.changed = True
|
||||
else:
|
||||
self.module.fail_json(msg=out)
|
||||
|
||||
def set_properties_if_changed(self):
|
||||
current_properties = self.get_current_properties()
|
||||
for prop, value in self.properties.iteritems():
|
||||
if current_properties[prop] != value:
|
||||
if prop in self.immutable_properties:
|
||||
self.module.fail_json(msg='Cannot change property %s after creation.' % prop)
|
||||
else:
|
||||
self.set_property(prop, value)
|
||||
|
||||
def get_current_properties(self):
|
||||
def get_properties_by_name(propname):
|
||||
cmd = [self.module.get_bin_path('zfs', True)]
|
||||
cmd += ['get', '-H', propname, self.name]
|
||||
rc, out, err = self.module.run_command(cmd)
|
||||
return [l.split('\t')[1:3] for l in out.splitlines()]
|
||||
properties = dict(get_properties_by_name('all'))
|
||||
if 'share.*' in properties:
|
||||
# Some ZFS pools list the sharenfs and sharesmb properties
|
||||
# hierarchically as share.nfs and share.smb respectively.
|
||||
del properties['share.*']
|
||||
for p, v in get_properties_by_name('share.all'):
|
||||
alias = p.replace('.', '') # share.nfs -> sharenfs (etc)
|
||||
properties[alias] = v
|
||||
return properties
|
||||
|
||||
def run_command(self, cmd):
|
||||
progname = cmd[0]
|
||||
cmd[0] = module.get_bin_path(progname, True)
|
||||
return module.run_command(cmd)
|
||||
|
||||
def main():
|
||||
|
||||
# FIXME: should use dict() constructor like other modules, required=False is default
|
||||
module = AnsibleModule(
|
||||
argument_spec = {
|
||||
'name': {'required': True},
|
||||
'state': {'required': True, 'choices':['present', 'absent']},
|
||||
'aclinherit': {'required': False, 'choices':['discard', 'noallow', 'restricted', 'passthrough', 'passthrough-x']},
|
||||
'aclmode': {'required': False, 'choices':['discard', 'groupmask', 'passthrough']},
|
||||
'atime': {'required': False, 'choices':['on', 'off']},
|
||||
'canmount': {'required': False, 'choices':['on', 'off', 'noauto']},
|
||||
'casesensitivity': {'required': False, 'choices':['sensitive', 'insensitive', 'mixed']},
|
||||
'checksum': {'required': False, 'choices':['on', 'off', 'fletcher2', 'fletcher4', 'sha256']},
|
||||
'compression': {'required': False, 'choices':['on', 'off', 'lzjb', 'gzip', 'gzip-1', 'gzip-2', 'gzip-3', 'gzip-4', 'gzip-5', 'gzip-6', 'gzip-7', 'gzip-8', 'gzip-9', 'lz4', 'zle']},
|
||||
'copies': {'required': False, 'choices':['1', '2', '3']},
|
||||
'dedup': {'required': False, 'choices':['on', 'off']},
|
||||
'devices': {'required': False, 'choices':['on', 'off']},
|
||||
'exec': {'required': False, 'choices':['on', 'off']},
|
||||
# Not supported
|
||||
#'groupquota': {'required': False},
|
||||
'jailed': {'required': False, 'choices':['on', 'off']},
|
||||
'logbias': {'required': False, 'choices':['latency', 'throughput']},
|
||||
'mountpoint': {'required': False},
|
||||
'nbmand': {'required': False, 'choices':['on', 'off']},
|
||||
'normalization': {'required': False, 'choices':['none', 'formC', 'formD', 'formKC', 'formKD']},
|
||||
'primarycache': {'required': False, 'choices':['all', 'none', 'metadata']},
|
||||
'quota': {'required': False},
|
||||
'readonly': {'required': False, 'choices':['on', 'off']},
|
||||
'recordsize': {'required': False},
|
||||
'refquota': {'required': False},
|
||||
'refreservation': {'required': False},
|
||||
'reservation': {'required': False},
|
||||
'secondarycache': {'required': False, 'choices':['all', 'none', 'metadata']},
|
||||
'setuid': {'required': False, 'choices':['on', 'off']},
|
||||
'shareiscsi': {'required': False, 'choices':['on', 'off']},
|
||||
'sharenfs': {'required': False},
|
||||
'sharesmb': {'required': False},
|
||||
'snapdir': {'required': False, 'choices':['hidden', 'visible']},
|
||||
'sync': {'required': False, 'choices':['on', 'off']},
|
||||
# Not supported
|
||||
#'userquota': {'required': False},
|
||||
'utf8only': {'required': False, 'choices':['on', 'off']},
|
||||
'volsize': {'required': False},
|
||||
'volblocksize': {'required': False},
|
||||
'vscan': {'required': False, 'choices':['on', 'off']},
|
||||
'xattr': {'required': False, 'choices':['on', 'off']},
|
||||
'zoned': {'required': False, 'choices':['on', 'off']},
|
||||
},
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
state = module.params.pop('state')
|
||||
name = module.params.pop('name')
|
||||
|
||||
# Get all valid zfs-properties
|
||||
properties = dict()
|
||||
for prop, value in module.params.iteritems():
|
||||
if prop in ['CHECKMODE']:
|
||||
continue
|
||||
if value:
|
||||
properties[prop] = value
|
||||
|
||||
result = {}
|
||||
result['name'] = name
|
||||
result['state'] = state
|
||||
|
||||
zfs=Zfs(module, name, properties)
|
||||
|
||||
if state == 'present':
|
||||
if zfs.exists():
|
||||
zfs.set_properties_if_changed()
|
||||
else:
|
||||
zfs.create()
|
||||
|
||||
elif state == 'absent':
|
||||
if zfs.exists():
|
||||
zfs.destroy()
|
||||
|
||||
result.update(zfs.properties)
|
||||
result['changed'] = zfs.changed
|
||||
module.exit_json(**result)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
||||
Reference in New Issue
Block a user