From 19c94d7380cf29f2d1b1e8f911d11c35d7e1c538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Wir=C3=A9n?= Date: Thu, 14 Feb 2013 14:16:08 +0100 Subject: [PATCH] Added zfs module --- library/zfs | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 library/zfs diff --git a/library/zfs b/library/zfs new file mode 100644 index 0000000000..04016773b4 --- /dev/null +++ b/library/zfs @@ -0,0 +1,260 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2013, Johan Wiren +# +# 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 . +# + +DOCUMENTATION = ''' +--- +module: zfs +short_description: Manage zfs +description: + - Manages ZFS file systems on Solaris and FreeBSD. Can manage file systems, volumes + and snapshots. Supports the following options from zfs(1M) (See the man page for + more information): + aclinherit + aclmode + atime + canmount + casesensitivity + checksum + compression + copies + dedup + devices + exec + jailed + logbias + mountpoint + nbmand + normalization + primarycache + quota + readonly + recordsize + refquota + refreservation + reservation + secondarycache + setuid + shareiscsi + sharenfs + sharesmb + snapdir + sync + utf8only + volsize + volblocksize + vscan + xattr + zoned +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] +examples: + - code: zfs name=rpool/myfs state=present + description: Create a new file system called myfs in pool rpool + - code: zfs name=rpool/myvol state=present volsize=10M + description: Create a new volume called myvol in pool rpool. + - code: zfs name=rpool/myfs@mysnapshot state=present + description: Create a snapshot of rpool/myfs file system. + - code: zfs name=rpool/myfs2 state=present snapdir=enabled + description: Create a new file system called myfs2 whith snapdir enabled +author: Johan Wiren +''' +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): + 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): + 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): + cmd = [self.module.get_bin_path('zfs', True)] + cmd.append('set') + cmd.append(prop + '=' + value) + 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_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()[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(): + 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']}, + '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']}, + } + ) + state = module.params.pop('state') + name = module.params.pop('name') + # Remaining items in module.params are zfs properties + + # Remove 'null' value properties + properties = dict() + for prop, value in module.params.iteritems(): + 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 +#<> +main()