mirror of https://github.com/ansible/ansible.git
Modules for gathering facts about ZFS datasets and pools (#19181)
* Add modules for gathering facts about ZFS datasets and pools * Move zfs module to storage/zfs subcategory * Replace dict.iteritems() with iteritems(dict) * Add ANSIBLE_METADATA Document return values Make imports explicit Use native YAML syntax in EXAMPLES * Add zfs_facts and zpool_facts modules to CHANGELOG.md * Add facts to return valuespull/20029/head
parent
3744764fae
commit
d9ced3f4d1
@ -0,0 +1,280 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2016, Adam Števko <adam.stevko@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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'status': ['preview'],
|
||||
'supported_by': 'community',
|
||||
'version': '1.0'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: zfs_facts
|
||||
short_description: Gather facts about ZFS datasets.
|
||||
description:
|
||||
- Gather facts from ZFS dataset properties.
|
||||
version_added: "2.3"
|
||||
author: Adam Števko (@xen0l)
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- ZFS dataset name.
|
||||
alias: [ "ds", "dataset" ]
|
||||
type: str
|
||||
required: yes
|
||||
recurse:
|
||||
description:
|
||||
- Specifies if properties for any children should be recursively
|
||||
displayed.
|
||||
type: bool
|
||||
default: False
|
||||
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 zfs(1M) man page.
|
||||
alias: [ "props" ]
|
||||
type: str
|
||||
default: all
|
||||
required: false
|
||||
type:
|
||||
description:
|
||||
- Specifies which datasets types to display. Multiple values have to be
|
||||
provided in comma-separated form.
|
||||
alias: [ "props" ]
|
||||
type: str
|
||||
default: all
|
||||
choices: [ 'all', 'filesystem', 'volume', 'snapshot', 'bookmark' ]
|
||||
required: false
|
||||
depth:
|
||||
description:
|
||||
- Specifiies recurion depth.
|
||||
type: int
|
||||
default: None
|
||||
required: false
|
||||
'''
|
||||
|
||||
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: string
|
||||
sample: rpool/var/spool
|
||||
parsable:
|
||||
description: if parsable output should be provided in machine friendly format.
|
||||
returned: if 'parsable' is set to True
|
||||
type: boolean
|
||||
sample: True
|
||||
recurse:
|
||||
description: if we should recurse over ZFS dataset
|
||||
returned: if 'recurse' is set to True
|
||||
type: boolean
|
||||
sample: True
|
||||
zfs_datasets:
|
||||
description: ZFS dataset facts
|
||||
returned: always
|
||||
type: string
|
||||
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"
|
||||
}
|
||||
'''
|
||||
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
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()
|
@ -0,0 +1,220 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2016, Adam Števko <adam.stevko@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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'status': ['preview'],
|
||||
'supported_by': 'community',
|
||||
'version': '1.0'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: zpool_facts
|
||||
short_description: Gather facts about ZFS pools.
|
||||
description:
|
||||
- Gather facts from ZFS pool properties.
|
||||
version_added: "2.3"
|
||||
author: Adam Števko (@xen0l)
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- ZFS pool name.
|
||||
alias: [ "pool", "zpool" ]
|
||||
type: str
|
||||
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.
|
||||
alias: [ "props" ]
|
||||
type: str
|
||||
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 = '''
|
||||
name:
|
||||
description: ZFS pool name
|
||||
returned: always
|
||||
type: string
|
||||
sample: rpool
|
||||
parsable:
|
||||
description: if parsable output should be provided in machine friendly format.
|
||||
returned: if 'parsable' is set to True
|
||||
type: boolean
|
||||
sample: True
|
||||
zfs_pools:
|
||||
description: ZFS pool facts
|
||||
returned: always
|
||||
type: string
|
||||
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": "-"
|
||||
}
|
||||
'''
|
||||
|
||||
import os
|
||||
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…
Reference in New Issue