community.general/lib/ansible/modules/system/filesystem.py

285 lines
9.4 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2013, Alexander Bulimov <lazywolf0@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 = '''
---
author:
- Alexander Bulimov (@abulimov)
module: filesystem
short_description: Makes file system on block device
description:
- This module creates file system.
version_added: "1.2"
options:
fstype:
choices: [ btrfs, ext2, ext3, ext4, ext4dev, lvm, reiserfs, xfs ]
description:
- File System type to be created.
- reiserfs support was added in 2.2.
- lvm support was added in 2.5.
required: yes
dev:
description:
- Target block device.
required: yes
force:
description:
- If C(yes), allows to create new filesystem on devices that already has filesystem.
type: bool
default: 'no'
resizefs:
description:
- If C(yes), if the block device and filessytem size differ, grow the filesystem into the space.
- Note, XFS Will only grow if mounted.
type: bool
default: 'no'
version_added: "2.0"
opts:
description:
- List of options to be passed to mkfs command.
notes:
- Uses mkfs command.
'''
EXAMPLES = '''
- name: Create a ext2 filesystem on /dev/sdb1
filesystem:
fstype: ext2
dev: /dev/sdb1
- name: Create a ext4 filesystem on /dev/sdb1 and check disk blocks
filesystem:
fstype: ext4
dev: /dev/sdb1
opts: -cc
'''
import os
from ansible.module_utils.basic import AnsibleModule
def _get_dev_size(dev, module):
""" Return size in bytes of device. Returns int """
blockdev_cmd = module.get_bin_path("blockdev", required=True)
rc, devsize_in_bytes, err = module.run_command("%s %s %s" % (blockdev_cmd, "--getsize64", dev))
return int(devsize_in_bytes)
def _get_fs_size(fssize_cmd, dev, module):
""" Return size in bytes of filesystem on device. Returns int """
cmd = module.get_bin_path(fssize_cmd, required=True)
if 'tune2fs' == fssize_cmd:
# Get Block count and Block size
rc, size, err = module.run_command("%s %s %s" % (cmd, '-l', dev))
if rc == 0:
for line in size.splitlines():
if 'Block count:' in line:
block_count = int(line.split(':')[1].strip())
elif 'Block size:' in line:
block_size = int(line.split(':')[1].strip())
break
else:
module.fail_json(msg="Failed to get block count and block size of %s with %s" % (dev, cmd), rc=rc, err=err)
elif 'xfs_growfs' == fssize_cmd:
# Get Block count and Block size
rc, size, err = module.run_command([cmd, '-n', dev])
if rc == 0:
for line in size.splitlines():
col = line.split('=')
if col[0].strip() == 'data':
if col[1].strip() != 'bsize':
module.fail_json(msg='Unexpected output format from xfs_growfs (could not locate "bsize")')
if col[2].split()[1] != 'blocks':
module.fail_json(msg='Unexpected output format from xfs_growfs (could not locate "blocks")')
block_size = int(col[2].split()[0])
block_count = int(col[3].split(',')[0])
break
else:
module.fail_json(msg="Failed to get block count and block size of %s with %s" % (dev, cmd), rc=rc, err=err)
elif 'btrfs' == fssize_cmd:
# ToDo
# There is no way to get the blocksize and blockcount for btrfs filesystems
block_size = 1
block_count = 1
elif 'pvs' == fssize_cmd:
rc, size, err = module.run_command([cmd, '--noheadings', '-o', 'pv_size', '--units', 'b', dev])
if rc == 0:
block_count = int(size[:-1])
block_size = 1
else:
module.fail_json(msg="Failed to get block count and block size of %s with %s" % (dev, cmd), rc=rc, err=err)
return block_size * block_count
def main():
friendly_names = {
'lvm': 'LVM2_member',
}
# There is no "single command" to manipulate filesystems, so we map them all out and their options
fs_cmd_map = {
'ext2': {
'mkfs': 'mkfs.ext2',
'grow': 'resize2fs',
'grow_flag': None,
'force_flag': '-F',
'fsinfo': 'tune2fs',
},
'ext3': {
'mkfs': 'mkfs.ext3',
'grow': 'resize2fs',
'grow_flag': None,
'force_flag': '-F',
'fsinfo': 'tune2fs',
},
'ext4': {
'mkfs': 'mkfs.ext4',
'grow': 'resize2fs',
'grow_flag': None,
'force_flag': '-F',
'fsinfo': 'tune2fs',
},
'reiserfs': {
'mkfs': 'mkfs.reiserfs',
'grow': 'resize_reiserfs',
'grow_flag': None,
'force_flag': '-f',
'fsinfo': 'reiserfstune',
},
'ext4dev': {
'mkfs': 'mkfs.ext4',
'grow': 'resize2fs',
'grow_flag': None,
'force_flag': '-F',
'fsinfo': 'tune2fs',
},
'xfs': {
'mkfs': 'mkfs.xfs',
'grow': 'xfs_growfs',
'grow_flag': None,
'force_flag': '-f',
'fsinfo': 'xfs_growfs',
},
'btrfs': {
'mkfs': 'mkfs.btrfs',
'grow': 'btrfs',
'grow_flag': 'filesystem resize',
'force_flag': '-f',
'fsinfo': 'btrfs',
},
'LVM2_member': {
'mkfs': 'pvcreate',
'grow': 'pvresize',
'grow_flag': None,
'force_flag': '-f',
'fsinfo': 'pvs',
}
}
module = AnsibleModule(
argument_spec=dict(
fstype=dict(type='str', required=True, aliases=['type'],
choices=fs_cmd_map.keys() + friendly_names.keys()),
dev=dict(type='str', required=True, aliases=['device']),
opts=dict(type='str'),
force=dict(type='bool', default=False),
resizefs=dict(type='bool', default=False),
),
supports_check_mode=True,
)
dev = module.params['dev']
fstype = module.params['fstype']
opts = module.params['opts']
force = module.boolean(module.params['force'])
resizefs = module.boolean(module.params['resizefs'])
if fstype in friendly_names:
fstype = friendly_names[fstype]
changed = False
try:
_ = fs_cmd_map[fstype]
except KeyError:
module.exit_json(changed=False, msg="WARNING: module does not support this filesystem yet. %s" % fstype)
mkfscmd = fs_cmd_map[fstype]['mkfs']
force_flag = fs_cmd_map[fstype]['force_flag']
growcmd = fs_cmd_map[fstype]['grow']
fssize_cmd = fs_cmd_map[fstype]['fsinfo']
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 and resizefs is False and not force:
module.exit_json(changed=False)
elif fs == fstype and resizefs is True:
# Get dev and fs size and compare
devsize_in_bytes = _get_dev_size(dev, module)
fssize_in_bytes = _get_fs_size(fssize_cmd, dev, module)
if fssize_in_bytes < devsize_in_bytes:
fs_smaller = True
else:
fs_smaller = False
if module.check_mode and fs_smaller:
module.exit_json(changed=True, msg="Resizing filesystem %s on device %s" % (fstype, dev))
elif module.check_mode and not fs_smaller:
module.exit_json(changed=False, msg="%s filesystem is using the whole device %s" % (fstype, dev))
elif fs_smaller:
cmd = module.get_bin_path(growcmd, required=True)
rc, out, err = module.run_command("%s %s" % (cmd, dev))
# Sadly there is no easy way to determine if this has changed. For now, just say "true" and move on.
# in the future, you would have to parse the output to determine this.
# thankfully, these are safe operations if no change is made.
if rc == 0:
module.exit_json(changed=True, msg=out)
else:
module.fail_json(msg="Resizing filesystem %s on device '%s' failed" % (fstype, dev), rc=rc, err=err)
else:
module.exit_json(changed=False, msg="%s filesystem is using the whole device %s" % (fstype, dev))
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(mkfscmd, required=True)
cmd = None
if opts is None:
cmd = "%s %s '%s'" % (mkfs, force_flag, dev)
else:
cmd = "%s %s %s '%s'" % (mkfs, 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)
if __name__ == '__main__':
main()