mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-24 13:50:22 -07:00
Initial commit
This commit is contained in:
commit
aebc1b03fd
4861 changed files with 812621 additions and 0 deletions
266
plugins/modules/storage/zfs/zfs.py
Normal file
266
plugins/modules/storage/zfs/zfs.py
Normal file
|
@ -0,0 +1,266 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2013, Johan Wiren <johan.wiren.se@gmail.com>
|
||||
# Copyright: (c) 2017, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: zfs
|
||||
short_description: Manage zfs
|
||||
description:
|
||||
- Manages ZFS file systems, volumes, clones and snapshots
|
||||
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. All parents/children
|
||||
will be created/destroyed as needed to reach the desired state.
|
||||
choices: [ absent, present ]
|
||||
required: true
|
||||
origin:
|
||||
description:
|
||||
- Snapshot from which to create a clone.
|
||||
extra_zfs_properties:
|
||||
description:
|
||||
- A dictionary of zfs properties to be set.
|
||||
- See the zfs(8) man page for more information.
|
||||
author:
|
||||
- Johan Wiren (@johanwiren)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create a new file system called myfs in pool rpool with the setuid property turned off
|
||||
zfs:
|
||||
name: rpool/myfs
|
||||
state: present
|
||||
extra_zfs_properties:
|
||||
setuid: off
|
||||
|
||||
- name: Create a new volume called myvol in pool rpool.
|
||||
zfs:
|
||||
name: rpool/myvol
|
||||
state: present
|
||||
extra_zfs_properties:
|
||||
volsize: 10M
|
||||
|
||||
- name: Create a snapshot of rpool/myfs file system.
|
||||
zfs:
|
||||
name: rpool/myfs@mysnapshot
|
||||
state: present
|
||||
|
||||
- name: Create a new file system called myfs2 with snapdir enabled
|
||||
zfs:
|
||||
name: rpool/myfs2
|
||||
state: present
|
||||
extra_zfs_properties:
|
||||
snapdir: enabled
|
||||
|
||||
- name: Create a new file system by cloning a snapshot
|
||||
zfs:
|
||||
name: rpool/cloned_fs
|
||||
state: present
|
||||
origin: rpool/myfs@mysnapshot
|
||||
|
||||
- name: Destroy a filesystem
|
||||
zfs:
|
||||
name: rpool/myfs
|
||||
state: absent
|
||||
'''
|
||||
|
||||
import os
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
class Zfs(object):
|
||||
|
||||
def __init__(self, module, name, properties):
|
||||
self.module = module
|
||||
self.name = name
|
||||
self.properties = properties
|
||||
self.changed = False
|
||||
self.zfs_cmd = module.get_bin_path('zfs', True)
|
||||
self.zpool_cmd = module.get_bin_path('zpool', True)
|
||||
self.pool = name.split('/')[0]
|
||||
self.is_solaris = os.uname()[0] == 'SunOS'
|
||||
self.is_openzfs = self.check_openzfs()
|
||||
self.enhanced_sharing = self.check_enhanced_sharing()
|
||||
|
||||
def check_openzfs(self):
|
||||
cmd = [self.zpool_cmd]
|
||||
cmd.extend(['get', 'version'])
|
||||
cmd.append(self.pool)
|
||||
(rc, out, err) = self.module.run_command(cmd, check_rc=True)
|
||||
version = out.splitlines()[-1].split()[2]
|
||||
if version == '-':
|
||||
return True
|
||||
if int(version) == 5000:
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_enhanced_sharing(self):
|
||||
if self.is_solaris and not self.is_openzfs:
|
||||
cmd = [self.zpool_cmd]
|
||||
cmd.extend(['get', 'version'])
|
||||
cmd.append(self.pool)
|
||||
(rc, out, err) = self.module.run_command(cmd, check_rc=True)
|
||||
version = out.splitlines()[-1].split()[2]
|
||||
if int(version) >= 34:
|
||||
return True
|
||||
return False
|
||||
|
||||
def exists(self):
|
||||
cmd = [self.zfs_cmd, 'list', '-t', 'all', 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
|
||||
origin = self.module.params.get('origin', None)
|
||||
cmd = [self.zfs_cmd]
|
||||
|
||||
if "@" in self.name:
|
||||
action = 'snapshot'
|
||||
elif origin:
|
||||
action = 'clone'
|
||||
else:
|
||||
action = 'create'
|
||||
|
||||
cmd.append(action)
|
||||
|
||||
if action in ['create', 'clone']:
|
||||
cmd += ['-p']
|
||||
|
||||
if properties:
|
||||
for prop, value in properties.items():
|
||||
if prop == 'volsize':
|
||||
cmd += ['-V', value]
|
||||
elif prop == 'volblocksize':
|
||||
cmd += ['-b', value]
|
||||
else:
|
||||
cmd += ['-o', '%s="%s"' % (prop, value)]
|
||||
if origin and action == 'clone':
|
||||
cmd.append(origin)
|
||||
cmd.append(self.name)
|
||||
(rc, out, err) = self.module.run_command(' '.join(cmd))
|
||||
if rc == 0:
|
||||
self.changed = True
|
||||
else:
|
||||
self.module.fail_json(msg=err)
|
||||
|
||||
def destroy(self):
|
||||
if self.module.check_mode:
|
||||
self.changed = True
|
||||
return
|
||||
cmd = [self.zfs_cmd, 'destroy', '-R', self.name]
|
||||
(rc, out, err) = self.module.run_command(' '.join(cmd))
|
||||
if rc == 0:
|
||||
self.changed = True
|
||||
else:
|
||||
self.module.fail_json(msg=err)
|
||||
|
||||
def set_property(self, prop, value):
|
||||
if self.module.check_mode:
|
||||
self.changed = True
|
||||
return
|
||||
cmd = [self.zfs_cmd, 'set', prop + '=' + str(value), self.name]
|
||||
(rc, out, err) = self.module.run_command(cmd)
|
||||
if rc == 0:
|
||||
self.changed = True
|
||||
else:
|
||||
self.module.fail_json(msg=err)
|
||||
|
||||
def set_properties_if_changed(self):
|
||||
current_properties = self.get_current_properties()
|
||||
for prop, value in self.properties.items():
|
||||
if current_properties.get(prop, None) != value:
|
||||
self.set_property(prop, value)
|
||||
|
||||
def get_current_properties(self):
|
||||
cmd = [self.zfs_cmd, 'get', '-H']
|
||||
if self.enhanced_sharing:
|
||||
cmd += ['-e']
|
||||
cmd += ['all', self.name]
|
||||
rc, out, err = self.module.run_command(" ".join(cmd))
|
||||
properties = dict()
|
||||
for prop, value, source in [l.split('\t')[1:4] for l in out.splitlines()]:
|
||||
if source == 'local':
|
||||
properties[prop] = value
|
||||
# Add alias for enhanced sharing properties
|
||||
if self.enhanced_sharing:
|
||||
properties['sharenfs'] = properties.get('share.nfs', None)
|
||||
properties['sharesmb'] = properties.get('share.smb', None)
|
||||
return properties
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(type='str', required=True),
|
||||
state=dict(type='str', required=True, choices=['absent', 'present']),
|
||||
origin=dict(type='str', default=None),
|
||||
extra_zfs_properties=dict(type='dict', default={}),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
state = module.params.get('state')
|
||||
name = module.params.get('name')
|
||||
|
||||
if module.params.get('origin') and '@' in name:
|
||||
module.fail_json(msg='cannot specify origin when operating on a snapshot')
|
||||
|
||||
# Reverse the boolification of zfs properties
|
||||
for prop, value in module.params['extra_zfs_properties'].items():
|
||||
if isinstance(value, bool):
|
||||
if value is True:
|
||||
module.params['extra_zfs_properties'][prop] = 'on'
|
||||
else:
|
||||
module.params['extra_zfs_properties'][prop] = 'off'
|
||||
else:
|
||||
module.params['extra_zfs_properties'][prop] = value
|
||||
|
||||
result = dict(
|
||||
name=name,
|
||||
state=state,
|
||||
)
|
||||
|
||||
zfs = Zfs(module, name, module.params['extra_zfs_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)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
267
plugins/modules/storage/zfs/zfs_delegate_admin.py
Normal file
267
plugins/modules/storage/zfs/zfs_delegate_admin.py
Normal file
|
@ -0,0 +1,267 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2015, Nate Coraor <nate@coraor.org>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'status': ['preview'],
|
||||
'supported_by': 'community',
|
||||
'metadata_version': '1.1'}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: zfs_delegate_admin
|
||||
short_description: Manage ZFS delegated administration (user admin privileges)
|
||||
description:
|
||||
- Manages ZFS file system delegated administration permissions, which allow unprivileged users to perform ZFS
|
||||
operations normally restricted to the superuser.
|
||||
- See the C(zfs allow) section of C(zfs(1M)) for detailed explanations of options.
|
||||
- This module attempts to adhere to the behavior of the command line tool as much as possible.
|
||||
requirements:
|
||||
- "A ZFS/OpenZFS implementation that supports delegation with `zfs allow`, including: Solaris >= 10, illumos (all
|
||||
versions), FreeBSD >= 8.0R, ZFS on Linux >= 0.7.0."
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- File system or volume name e.g. C(rpool/myfs).
|
||||
required: true
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Whether to allow (C(present)), or unallow (C(absent)) a permission.
|
||||
- When set to C(present), at least one "entity" param of I(users), I(groups), or I(everyone) are required.
|
||||
- When set to C(absent), removes permissions from the specified entities, or removes all permissions if no entity params are specified.
|
||||
required: true
|
||||
choices: [ absent, present ]
|
||||
default: present
|
||||
users:
|
||||
description:
|
||||
- List of users to whom permission(s) should be granted.
|
||||
type: list
|
||||
groups:
|
||||
description:
|
||||
- List of groups to whom permission(s) should be granted.
|
||||
type: list
|
||||
everyone:
|
||||
description:
|
||||
- Apply permissions to everyone.
|
||||
type: bool
|
||||
default: no
|
||||
permissions:
|
||||
description:
|
||||
- The list of permission(s) to delegate (required if C(state) is C(present)).
|
||||
type: list
|
||||
choices: [ allow, clone, create, destroy, mount, promote, readonly, receive, rename, rollback, send, share, snapshot, unallow ]
|
||||
local:
|
||||
description:
|
||||
- Apply permissions to C(name) locally (C(zfs allow -l)).
|
||||
type: bool
|
||||
descendents:
|
||||
description:
|
||||
- Apply permissions to C(name)'s descendents (C(zfs allow -d)).
|
||||
type: bool
|
||||
recursive:
|
||||
description:
|
||||
- Unallow permissions recursively (ignored when C(state) is C(present)).
|
||||
type: bool
|
||||
default: no
|
||||
author:
|
||||
- Nate Coraor (@natefoo)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Grant `zfs allow` and `unallow` permission to the `adm` user with the default local+descendents scope
|
||||
zfs_delegate_admin:
|
||||
name: rpool/myfs
|
||||
users: adm
|
||||
permissions: allow,unallow
|
||||
|
||||
- name: Grant `zfs send` to everyone, plus the group `backup`
|
||||
zfs_delegate_admin:
|
||||
name: rpool/myvol
|
||||
groups: backup
|
||||
everyone: yes
|
||||
permissions: send
|
||||
|
||||
- name: Grant `zfs send,receive` to users `foo` and `bar` with local scope only
|
||||
zfs_delegate_admin:
|
||||
name: rpool/myfs
|
||||
users: foo,bar
|
||||
permissions: send,receive
|
||||
local: yes
|
||||
|
||||
- name: Revoke all permissions from everyone (permissions specifically assigned to users and groups remain)
|
||||
- zfs_delegate_admin:
|
||||
name: rpool/myfs
|
||||
everyone: yes
|
||||
state: absent
|
||||
'''
|
||||
|
||||
# This module does not return anything other than the standard
|
||||
# changed/state/msg/stdout
|
||||
RETURN = '''
|
||||
'''
|
||||
|
||||
from itertools import product
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
class ZfsDelegateAdmin(object):
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.name = module.params.get('name')
|
||||
self.state = module.params.get('state')
|
||||
self.users = module.params.get('users')
|
||||
self.groups = module.params.get('groups')
|
||||
self.everyone = module.params.get('everyone')
|
||||
self.perms = module.params.get('permissions')
|
||||
self.scope = None
|
||||
self.changed = False
|
||||
self.initial_perms = None
|
||||
self.subcommand = 'allow'
|
||||
self.recursive_opt = []
|
||||
self.run_method = self.update
|
||||
|
||||
self.setup(module)
|
||||
|
||||
def setup(self, module):
|
||||
""" Validate params and set up for run.
|
||||
"""
|
||||
if self.state == 'absent':
|
||||
self.subcommand = 'unallow'
|
||||
if module.params.get('recursive'):
|
||||
self.recursive_opt = ['-r']
|
||||
|
||||
local = module.params.get('local')
|
||||
descendents = module.params.get('descendents')
|
||||
if (local and descendents) or (not local and not descendents):
|
||||
self.scope = 'ld'
|
||||
elif local:
|
||||
self.scope = 'l'
|
||||
elif descendents:
|
||||
self.scope = 'd'
|
||||
else:
|
||||
self.module.fail_json(msg='Impossible value for local and descendents')
|
||||
|
||||
if not (self.users or self.groups or self.everyone):
|
||||
if self.state == 'present':
|
||||
self.module.fail_json(msg='One of `users`, `groups`, or `everyone` must be set')
|
||||
elif self.state == 'absent':
|
||||
self.run_method = self.clear
|
||||
# ansible ensures the else cannot happen here
|
||||
|
||||
self.zfs_path = module.get_bin_path('zfs', True)
|
||||
|
||||
@property
|
||||
def current_perms(self):
|
||||
""" Parse the output of `zfs allow <name>` to retrieve current permissions.
|
||||
"""
|
||||
out = self.run_zfs_raw(subcommand='allow')
|
||||
perms = {
|
||||
'l': {'u': {}, 'g': {}, 'e': []},
|
||||
'd': {'u': {}, 'g': {}, 'e': []},
|
||||
'ld': {'u': {}, 'g': {}, 'e': []},
|
||||
}
|
||||
linemap = {
|
||||
'Local permissions:': 'l',
|
||||
'Descendent permissions:': 'd',
|
||||
'Local+Descendent permissions:': 'ld',
|
||||
}
|
||||
scope = None
|
||||
for line in out.splitlines():
|
||||
scope = linemap.get(line, scope)
|
||||
if not scope:
|
||||
continue
|
||||
try:
|
||||
if line.startswith('\tuser ') or line.startswith('\tgroup '):
|
||||
ent_type, ent, cur_perms = line.split()
|
||||
perms[scope][ent_type[0]][ent] = cur_perms.split(',')
|
||||
elif line.startswith('\teveryone '):
|
||||
perms[scope]['e'] = line.split()[1].split(',')
|
||||
except ValueError:
|
||||
self.module.fail_json(msg="Cannot parse user/group permission output by `zfs allow`: '%s'" % line)
|
||||
return perms
|
||||
|
||||
def run_zfs_raw(self, subcommand=None, args=None):
|
||||
""" Run a raw zfs command, fail on error.
|
||||
"""
|
||||
cmd = [self.zfs_path, subcommand or self.subcommand] + (args or []) + [self.name]
|
||||
rc, out, err = self.module.run_command(cmd)
|
||||
if rc:
|
||||
self.module.fail_json(msg='Command `%s` failed: %s' % (' '.join(cmd), err))
|
||||
return out
|
||||
|
||||
def run_zfs(self, args):
|
||||
""" Run zfs allow/unallow with appropriate options as per module arguments.
|
||||
"""
|
||||
args = self.recursive_opt + ['-' + self.scope] + args
|
||||
if self.perms:
|
||||
args.append(','.join(self.perms))
|
||||
return self.run_zfs_raw(args=args)
|
||||
|
||||
def clear(self):
|
||||
""" Called by run() to clear all permissions.
|
||||
"""
|
||||
changed = False
|
||||
stdout = ''
|
||||
for scope, ent_type in product(('ld', 'l', 'd'), ('u', 'g')):
|
||||
for ent in self.initial_perms[scope][ent_type].keys():
|
||||
stdout += self.run_zfs(['-%s' % ent_type, ent])
|
||||
changed = True
|
||||
for scope in ('ld', 'l', 'd'):
|
||||
if self.initial_perms[scope]['e']:
|
||||
stdout += self.run_zfs(['-e'])
|
||||
changed = True
|
||||
return (changed, stdout)
|
||||
|
||||
def update(self):
|
||||
""" Update permissions as per module arguments.
|
||||
"""
|
||||
stdout = ''
|
||||
for ent_type, entities in (('u', self.users), ('g', self.groups)):
|
||||
if entities:
|
||||
stdout += self.run_zfs(['-%s' % ent_type, ','.join(entities)])
|
||||
if self.everyone:
|
||||
stdout += self.run_zfs(['-e'])
|
||||
return (self.initial_perms != self.current_perms, stdout)
|
||||
|
||||
def run(self):
|
||||
""" Run an operation, return results for Ansible.
|
||||
"""
|
||||
exit_args = {'state': self.state}
|
||||
self.initial_perms = self.current_perms
|
||||
exit_args['changed'], stdout = self.run_method()
|
||||
if exit_args['changed']:
|
||||
exit_args['msg'] = 'ZFS delegated admin permissions updated'
|
||||
exit_args['stdout'] = stdout
|
||||
self.module.exit_json(**exit_args)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(type='str', required=True),
|
||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||
users=dict(type='list'),
|
||||
groups=dict(type='list'),
|
||||
everyone=dict(type='bool', default=False),
|
||||
permissions=dict(type='list',
|
||||
choices=['allow', 'clone', 'create', 'destroy', 'mount', 'promote', 'readonly', 'receive',
|
||||
'rename', 'rollback', 'send', 'share', 'snapshot', 'unallow']),
|
||||
local=dict(type='bool'),
|
||||
descendents=dict(type='bool'),
|
||||
recursive=dict(type='bool', default=False),
|
||||
),
|
||||
supports_check_mode=False,
|
||||
required_if=[('state', 'present', ['permissions'])],
|
||||
)
|
||||
zfs_delegate_admin = ZfsDelegateAdmin(module)
|
||||
zfs_delegate_admin.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
265
plugins/modules/storage/zfs/zfs_facts.py
Normal file
265
plugins/modules/storage/zfs/zfs_facts.py
Normal file
|
@ -0,0 +1,265 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2016, Adam Števko <adam.stevko@gmail.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: zfs_facts
|
||||
short_description: Gather facts about ZFS datasets.
|
||||
description:
|
||||
- Gather facts from ZFS dataset properties.
|
||||
author: Adam Števko (@xen0l)
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- ZFS dataset name.
|
||||
required: yes
|
||||
aliases: [ "ds", "dataset" ]
|
||||
recurse:
|
||||
description:
|
||||
- Specifies if properties for any children should be recursively
|
||||
displayed.
|
||||
type: bool
|
||||
default: 'no'
|
||||
parsable:
|
||||
description:
|
||||
- Specifies if property values should be displayed in machine
|
||||
friendly format.
|
||||
type: bool
|
||||
default: 'no'
|
||||
properties:
|
||||
description:
|
||||
- Specifies which dataset properties should be queried in comma-separated format.
|
||||
For more information about dataset properties, check zfs(1M) man page.
|
||||
default: all
|
||||
aliases: [ "props" ]
|
||||
type:
|
||||
description:
|
||||
- Specifies which datasets types to display. Multiple values have to be
|
||||
provided in comma-separated form.
|
||||
choices: [ 'all', 'filesystem', 'volume', 'snapshot', 'bookmark' ]
|
||||
default: all
|
||||
depth:
|
||||
description:
|
||||
- Specifies recursion depth.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Gather facts about ZFS dataset rpool/export/home
|
||||
zfs_facts:
|
||||
dataset: rpool/export/home
|
||||
|
||||
- name: Report space usage on ZFS filesystems under data/home
|
||||
zfs_facts:
|
||||
name: data/home
|
||||
recurse: yes
|
||||
type: filesystem
|
||||
|
||||
- debug:
|
||||
msg: 'ZFS dataset {{ item.name }} consumes {{ item.used }} of disk space.'
|
||||
with_items: '{{ ansible_zfs_datasets }}'
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
name:
|
||||
description: ZFS dataset name
|
||||
returned: always
|
||||
type: str
|
||||
sample: rpool/var/spool
|
||||
parsable:
|
||||
description: if parsable output should be provided in machine friendly format.
|
||||
returned: if 'parsable' is set to True
|
||||
type: bool
|
||||
sample: True
|
||||
recurse:
|
||||
description: if we should recurse over ZFS dataset
|
||||
returned: if 'recurse' is set to True
|
||||
type: bool
|
||||
sample: True
|
||||
zfs_datasets:
|
||||
description: ZFS dataset facts
|
||||
returned: always
|
||||
type: str
|
||||
sample:
|
||||
{
|
||||
"aclinherit": "restricted",
|
||||
"aclmode": "discard",
|
||||
"atime": "on",
|
||||
"available": "43.8G",
|
||||
"canmount": "on",
|
||||
"casesensitivity": "sensitive",
|
||||
"checksum": "on",
|
||||
"compression": "off",
|
||||
"compressratio": "1.00x",
|
||||
"copies": "1",
|
||||
"creation": "Thu Jun 16 11:37 2016",
|
||||
"dedup": "off",
|
||||
"devices": "on",
|
||||
"exec": "on",
|
||||
"filesystem_count": "none",
|
||||
"filesystem_limit": "none",
|
||||
"logbias": "latency",
|
||||
"logicalreferenced": "18.5K",
|
||||
"logicalused": "3.45G",
|
||||
"mlslabel": "none",
|
||||
"mounted": "yes",
|
||||
"mountpoint": "/rpool",
|
||||
"name": "rpool",
|
||||
"nbmand": "off",
|
||||
"normalization": "none",
|
||||
"org.openindiana.caiman:install": "ready",
|
||||
"primarycache": "all",
|
||||
"quota": "none",
|
||||
"readonly": "off",
|
||||
"recordsize": "128K",
|
||||
"redundant_metadata": "all",
|
||||
"refcompressratio": "1.00x",
|
||||
"referenced": "29.5K",
|
||||
"refquota": "none",
|
||||
"refreservation": "none",
|
||||
"reservation": "none",
|
||||
"secondarycache": "all",
|
||||
"setuid": "on",
|
||||
"sharenfs": "off",
|
||||
"sharesmb": "off",
|
||||
"snapdir": "hidden",
|
||||
"snapshot_count": "none",
|
||||
"snapshot_limit": "none",
|
||||
"sync": "standard",
|
||||
"type": "filesystem",
|
||||
"used": "4.41G",
|
||||
"usedbychildren": "4.41G",
|
||||
"usedbydataset": "29.5K",
|
||||
"usedbyrefreservation": "0",
|
||||
"usedbysnapshots": "0",
|
||||
"utf8only": "off",
|
||||
"version": "5",
|
||||
"vscan": "off",
|
||||
"written": "29.5K",
|
||||
"xattr": "on",
|
||||
"zoned": "off"
|
||||
}
|
||||
'''
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
|
||||
SUPPORTED_TYPES = ['all', 'filesystem', 'volume', 'snapshot', 'bookmark']
|
||||
|
||||
|
||||
class ZFSFacts(object):
|
||||
def __init__(self, module):
|
||||
|
||||
self.module = module
|
||||
|
||||
self.name = module.params['name']
|
||||
self.recurse = module.params['recurse']
|
||||
self.parsable = module.params['parsable']
|
||||
self.properties = module.params['properties']
|
||||
self.type = module.params['type']
|
||||
self.depth = module.params['depth']
|
||||
|
||||
self._datasets = defaultdict(dict)
|
||||
self.facts = []
|
||||
|
||||
def dataset_exists(self):
|
||||
cmd = [self.module.get_bin_path('zfs')]
|
||||
|
||||
cmd.append('list')
|
||||
cmd.append(self.name)
|
||||
|
||||
(rc, out, err) = self.module.run_command(cmd)
|
||||
|
||||
if rc == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_facts(self):
|
||||
cmd = [self.module.get_bin_path('zfs')]
|
||||
|
||||
cmd.append('get')
|
||||
cmd.append('-H')
|
||||
if self.parsable:
|
||||
cmd.append('-p')
|
||||
if self.recurse:
|
||||
cmd.append('-r')
|
||||
if int(self.depth) != 0:
|
||||
cmd.append('-d')
|
||||
cmd.append('%s' % self.depth)
|
||||
if self.type:
|
||||
cmd.append('-t')
|
||||
cmd.append(self.type)
|
||||
cmd.append('-o')
|
||||
cmd.append('name,property,value')
|
||||
cmd.append(self.properties)
|
||||
cmd.append(self.name)
|
||||
|
||||
(rc, out, err) = self.module.run_command(cmd)
|
||||
|
||||
if rc == 0:
|
||||
for line in out.splitlines():
|
||||
dataset, property, value = line.split('\t')
|
||||
|
||||
self._datasets[dataset].update({property: value})
|
||||
|
||||
for k, v in iteritems(self._datasets):
|
||||
v.update({'name': k})
|
||||
self.facts.append(v)
|
||||
|
||||
return {'ansible_zfs_datasets': self.facts}
|
||||
else:
|
||||
self.module.fail_json(msg='Error while trying to get facts about ZFS dataset: %s' % self.name,
|
||||
stderr=err,
|
||||
rc=rc)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(required=True, aliases=['ds', 'dataset'], type='str'),
|
||||
recurse=dict(required=False, default=False, type='bool'),
|
||||
parsable=dict(required=False, default=False, type='bool'),
|
||||
properties=dict(required=False, default='all', type='str'),
|
||||
type=dict(required=False, default='all', type='str', choices=SUPPORTED_TYPES),
|
||||
depth=dict(required=False, default=0, type='int')
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
zfs_facts = ZFSFacts(module)
|
||||
|
||||
result = {}
|
||||
result['changed'] = False
|
||||
result['name'] = zfs_facts.name
|
||||
|
||||
if zfs_facts.parsable:
|
||||
result['parsable'] = zfs_facts.parsable
|
||||
|
||||
if zfs_facts.recurse:
|
||||
result['recurse'] = zfs_facts.recurse
|
||||
|
||||
if zfs_facts.dataset_exists():
|
||||
result['ansible_facts'] = zfs_facts.get_facts()
|
||||
else:
|
||||
module.fail_json(msg='ZFS dataset %s does not exist!' % zfs_facts.name)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
214
plugins/modules/storage/zfs/zpool_facts.py
Normal file
214
plugins/modules/storage/zfs/zpool_facts.py
Normal file
|
@ -0,0 +1,214 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2016, Adam Števko <adam.stevko@gmail.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: zpool_facts
|
||||
short_description: Gather facts about ZFS pools.
|
||||
description:
|
||||
- Gather facts from ZFS pool properties.
|
||||
author: Adam Števko (@xen0l)
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- ZFS pool name.
|
||||
aliases: [ "pool", "zpool" ]
|
||||
required: false
|
||||
parsable:
|
||||
description:
|
||||
- Specifies if property values should be displayed in machine
|
||||
friendly format.
|
||||
type: bool
|
||||
default: False
|
||||
required: false
|
||||
properties:
|
||||
description:
|
||||
- Specifies which dataset properties should be queried in comma-separated format.
|
||||
For more information about dataset properties, check zpool(1M) man page.
|
||||
aliases: [ "props" ]
|
||||
default: all
|
||||
required: false
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Gather facts about ZFS pool rpool
|
||||
- zpool_facts: pool=rpool
|
||||
|
||||
# Gather space usage about all imported ZFS pools
|
||||
- zpool_facts: properties='free,size'
|
||||
|
||||
- debug: msg='ZFS pool {{ item.name }} has {{ item.free }} free space out of {{ item.size }}.'
|
||||
with_items: '{{ ansible_zfs_pools }}'
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ansible_facts:
|
||||
description: Dictionary containing all the detailed information about the ZFS pool facts
|
||||
returned: always
|
||||
type: complex
|
||||
contains:
|
||||
ansible_zfs_pools:
|
||||
description: ZFS pool facts
|
||||
returned: always
|
||||
type: str
|
||||
sample:
|
||||
{
|
||||
"allocated": "3.46G",
|
||||
"altroot": "-",
|
||||
"autoexpand": "off",
|
||||
"autoreplace": "off",
|
||||
"bootfs": "rpool/ROOT/openindiana",
|
||||
"cachefile": "-",
|
||||
"capacity": "6%",
|
||||
"comment": "-",
|
||||
"dedupditto": "0",
|
||||
"dedupratio": "1.00x",
|
||||
"delegation": "on",
|
||||
"expandsize": "-",
|
||||
"failmode": "wait",
|
||||
"feature@async_destroy": "enabled",
|
||||
"feature@bookmarks": "enabled",
|
||||
"feature@edonr": "enabled",
|
||||
"feature@embedded_data": "active",
|
||||
"feature@empty_bpobj": "active",
|
||||
"feature@enabled_txg": "active",
|
||||
"feature@extensible_dataset": "enabled",
|
||||
"feature@filesystem_limits": "enabled",
|
||||
"feature@hole_birth": "active",
|
||||
"feature@large_blocks": "enabled",
|
||||
"feature@lz4_compress": "active",
|
||||
"feature@multi_vdev_crash_dump": "enabled",
|
||||
"feature@sha512": "enabled",
|
||||
"feature@skein": "enabled",
|
||||
"feature@spacemap_histogram": "active",
|
||||
"fragmentation": "3%",
|
||||
"free": "46.3G",
|
||||
"freeing": "0",
|
||||
"guid": "15729052870819522408",
|
||||
"health": "ONLINE",
|
||||
"leaked": "0",
|
||||
"listsnapshots": "off",
|
||||
"name": "rpool",
|
||||
"readonly": "off",
|
||||
"size": "49.8G",
|
||||
"version": "-"
|
||||
}
|
||||
name:
|
||||
description: ZFS pool name
|
||||
returned: always
|
||||
type: str
|
||||
sample: rpool
|
||||
parsable:
|
||||
description: if parsable output should be provided in machine friendly format.
|
||||
returned: if 'parsable' is set to True
|
||||
type: bool
|
||||
sample: True
|
||||
'''
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
class ZPoolFacts(object):
|
||||
def __init__(self, module):
|
||||
|
||||
self.module = module
|
||||
|
||||
self.name = module.params['name']
|
||||
self.parsable = module.params['parsable']
|
||||
self.properties = module.params['properties']
|
||||
|
||||
self._pools = defaultdict(dict)
|
||||
self.facts = []
|
||||
|
||||
def pool_exists(self):
|
||||
cmd = [self.module.get_bin_path('zpool')]
|
||||
|
||||
cmd.append('list')
|
||||
cmd.append(self.name)
|
||||
|
||||
(rc, out, err) = self.module.run_command(cmd)
|
||||
|
||||
if rc == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_facts(self):
|
||||
cmd = [self.module.get_bin_path('zpool')]
|
||||
|
||||
cmd.append('get')
|
||||
cmd.append('-H')
|
||||
if self.parsable:
|
||||
cmd.append('-p')
|
||||
cmd.append('-o')
|
||||
cmd.append('name,property,value')
|
||||
cmd.append(self.properties)
|
||||
if self.name:
|
||||
cmd.append(self.name)
|
||||
|
||||
(rc, out, err) = self.module.run_command(cmd)
|
||||
|
||||
if rc == 0:
|
||||
for line in out.splitlines():
|
||||
pool, property, value = line.split('\t')
|
||||
|
||||
self._pools[pool].update({property: value})
|
||||
|
||||
for k, v in iteritems(self._pools):
|
||||
v.update({'name': k})
|
||||
self.facts.append(v)
|
||||
|
||||
return {'ansible_zfs_pools': self.facts}
|
||||
else:
|
||||
self.module.fail_json(msg='Error while trying to get facts about ZFS pool: %s' % self.name,
|
||||
stderr=err,
|
||||
rc=rc)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(required=False, aliases=['pool', 'zpool'], type='str'),
|
||||
parsable=dict(required=False, default=False, type='bool'),
|
||||
properties=dict(required=False, default='all', type='str'),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
zpool_facts = ZPoolFacts(module)
|
||||
|
||||
result = {}
|
||||
result['changed'] = False
|
||||
result['name'] = zpool_facts.name
|
||||
|
||||
if zpool_facts.parsable:
|
||||
result['parsable'] = zpool_facts.parsable
|
||||
|
||||
if zpool_facts.name is not None:
|
||||
if zpool_facts.pool_exists():
|
||||
result['ansible_facts'] = zpool_facts.get_facts()
|
||||
else:
|
||||
module.fail_json(msg='ZFS pool %s does not exist!' % zpool_facts.name)
|
||||
else:
|
||||
result['ansible_facts'] = zpool_facts.get_facts()
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Add table
Add a link
Reference in a new issue