ansible/library/system/zfs
2013-09-30 14:20:21 +02:00

412 lines
13 KiB
Python

#!/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]
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):
cmd = [self.module.get_bin_path('zfs', True)]
cmd.append('get -H all')
cmd.append(self.name)
rc, out, err = self.module.run_command(' '.join(cmd))
properties = dict()
for l in out.splitlines():
p, v = l.split('\t')[1:3]
properties[p] = 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']},
'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)
# include magic from lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()