1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Add vfat support for the filesystem module (#23527)

* Add fat filesystem support

fatresize is temporarily disabled

* Refactor Filesystem.get_dev_size

For more sharing with vFAT class

* Fix filesystem tests on some OSs

I think this is due to older mke2fs on those systems.

* Fix vFAT command on FreeBSD

newfs doesn't seem to work on image files

* Refactor filesystem.grow()

Split out grow_cmd generation and Device operations

* Use swap as unsupported filesystem

Except FreeBSD, which doesn't have mkswap

* Be consistent about str(dev) vs dev.path

Prefer str(dev), this works transparently with '%s' formatting.

* Enable vfat resize, only test fatresize >= 1.0.4

Lower versions have a segfault bug.

* Only install fatresize where available

FreeBSD, OpenSUSE, RHEL and CentOS < 7 don't ship it.
This commit is contained in:
Kai 2018-02-06 10:56:43 +00:00 committed by Michael Scherer
parent 67c217398c
commit f30a08d049
5 changed files with 83 additions and 37 deletions

View file

@ -22,12 +22,13 @@ description:
version_added: "1.2" version_added: "1.2"
options: options:
fstype: fstype:
choices: [ btrfs, ext2, ext3, ext4, ext4dev, lvm, reiserfs, xfs ] choices: [ btrfs, ext2, ext3, ext4, ext4dev, lvm, reiserfs, xfs, vfat ]
description: description:
- Filesystem type to be created. - Filesystem type to be created.
- reiserfs support was added in 2.2. - reiserfs support was added in 2.2.
- lvm support was added in 2.5. - lvm support was added in 2.5.
- since 2.5, I(dev) can be an image file. - since 2.5, I(dev) can be an image file.
- vfat support was added in 2.5
required: yes required: yes
dev: dev:
description: description:
@ -41,8 +42,9 @@ options:
resizefs: resizefs:
description: description:
- If C(yes), if the block device and filesytem size differ, grow the filesystem into the space. - If C(yes), if the block device and filesytem size differ, grow the filesystem into the space.
- Supported for C(ext2), C(ext3), C(ext4), C(ext4dev), C(lvm) and C(xfs) filesystems. - Supported for C(ext2), C(ext3), C(ext4), C(ext4dev), C(lvm), C(xfs) and C(vfat) filesystems.
- XFS Will only grow if mounted. - XFS Will only grow if mounted.
- vFAT will likely fail if fatresize < 1.04.
type: bool type: bool
default: 'no' default: 'no'
version_added: "2.0" version_added: "2.0"
@ -74,8 +76,28 @@ import os
import re import re
import stat import stat
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule, get_platform
from ansible.module_utils.six import viewkeys
class Device(object):
def __init__(self, module, path):
self.module = module
self.path = path
def size(self):
""" Return size in bytes of device. Returns int """
statinfo = os.stat(self.path)
if stat.S_ISBLK(statinfo.st_mode):
blockdev_cmd = self.module.get_bin_path("blockdev", required=True)
_, devsize_in_bytes, _ = self.module.run_command([blockdev_cmd, "--getsize64", self.path], check_rc=True)
return int(devsize_in_bytes)
elif os.path.isfile(self.path):
return os.path.getsize(self.path)
else:
self.module.fail_json(changed=False, msg="Target device not supported: %s" % self)
def __str__(self):
return self.path
class Filesystem(object): class Filesystem(object):
@ -91,12 +113,6 @@ class Filesystem(object):
def fstype(self): def fstype(self):
return type(self).__name__ return type(self).__name__
def get_dev_size(self, dev):
""" Return size in bytes of device. Returns int """
blockdev_cmd = self.module.get_bin_path("blockdev", required=True)
_, devsize_in_bytes, _ = self.module.run_command("%s %s %s" % (blockdev_cmd, "--getsize64", dev), check_rc=True)
return int(devsize_in_bytes)
def get_fs_size(self, dev): def get_fs_size(self, dev):
""" Return size in bytes of filesystem on device. Returns int """ """ Return size in bytes of filesystem on device. Returns int """
raise NotImplementedError() raise NotImplementedError()
@ -112,32 +128,26 @@ class Filesystem(object):
cmd = "%s %s %s '%s'" % (mkfs, self.MKFS_FORCE_FLAGS, opts, dev) cmd = "%s %s %s '%s'" % (mkfs, self.MKFS_FORCE_FLAGS, opts, dev)
self.module.run_command(cmd, check_rc=True) self.module.run_command(cmd, check_rc=True)
def grow_cmd(self, dev):
cmd = self.module.get_bin_path(self.GROW, required=True)
return [cmd, str(dev)]
def grow(self, dev): def grow(self, dev):
"""Get dev and fs size and compare. Returns stdout of used command.""" """Get dev and fs size and compare. Returns stdout of used command."""
statinfo = os.stat(dev) devsize_in_bytes = dev.size()
if stat.S_ISBLK(statinfo.st_mode):
devsize_in_bytes = self.get_dev_size(dev)
elif os.path.isfile(dev):
devsize_in_bytes = os.path.getsize(dev)
else:
self.module.fail_json(changed=False, msg="Target device not supported: %r." % dev)
try: try:
fssize_in_bytes = self.get_fs_size(dev) fssize_in_bytes = self.get_fs_size(dev)
except NotImplementedError: except NotImplementedError:
self.module.fail_json(changed=False, msg="module does not support resizing %s filesystem yet." % self.fstype) self.module.fail_json(changed=False, msg="module does not support resizing %s filesystem yet." % self.fstype)
fs_smaller = fssize_in_bytes < devsize_in_bytes
if self.module.check_mode and fs_smaller: if not fssize_in_bytes < devsize_in_bytes:
self.module.exit_json(changed=False, msg="%s filesystem is using the whole device %s" % (self.fstype, dev))
elif self.module.check_mode:
self.module.exit_json(changed=True, msg="Resizing filesystem %s on device %s" % (self.fstype, dev)) self.module.exit_json(changed=True, msg="Resizing filesystem %s on device %s" % (self.fstype, dev))
elif self.module.check_mode and not fs_smaller:
self.module.exit_json(changed=False, msg="%s filesystem is using the whole device %s" % (self.fstype, dev))
elif fs_smaller:
cmd = self.module.get_bin_path(self.GROW, required=True)
_, out, _ = self.module.run_command("%s %s" % (cmd, dev), check_rc=True)
return out
else: else:
self.module.exit_json(changed=False, msg="%s filesystem is using the whole device %s" % (self.fstype, dev)) _, out, _ = self.module.run_command(self.grow_cmd(dev), check_rc=True)
return out
class Ext(Filesystem): class Ext(Filesystem):
@ -147,7 +157,7 @@ class Ext(Filesystem):
def get_fs_size(self, dev): def get_fs_size(self, dev):
cmd = self.module.get_bin_path('tune2fs', required=True) cmd = self.module.get_bin_path('tune2fs', required=True)
# Get Block count and Block size # Get Block count and Block size
_, size, _ = self.module.run_command([cmd, '-l', dev], check_rc=True) _, size, _ = self.module.run_command([cmd, '-l', str(dev)], check_rc=True)
for line in size.splitlines(): for line in size.splitlines():
if 'Block count:' in line: if 'Block count:' in line:
block_count = int(line.split(':')[1].strip()) block_count = int(line.split(':')[1].strip())
@ -175,7 +185,7 @@ class XFS(Filesystem):
def get_fs_size(self, dev): def get_fs_size(self, dev):
cmd = self.module.get_bin_path('xfs_growfs', required=True) cmd = self.module.get_bin_path('xfs_growfs', required=True)
_, size, _ = self.module.run_command([cmd, '-n', dev], check_rc=True) _, size, _ = self.module.run_command([cmd, '-n', str(dev)], check_rc=True)
for line in size.splitlines(): for line in size.splitlines():
col = line.split('=') col = line.split('=')
if col[0].strip() == 'data': if col[0].strip() == 'data':
@ -215,6 +225,27 @@ class Btrfs(Filesystem):
self.module.warn('Unable to identify mkfs.btrfs version (%r, %r)' % (stdout, stderr)) self.module.warn('Unable to identify mkfs.btrfs version (%r, %r)' % (stdout, stderr))
class VFAT(Filesystem):
if get_platform() == 'FreeBSD':
MKFS = "newfs_msdos"
else:
MKFS = 'mkfs.vfat'
GROW = 'fatresize'
def get_fs_size(self, dev):
cmd = self.module.get_bin_path(self.GROW, required=True)
_, output, _ = self.module.run_command([cmd, '--info', str(dev)], check_rc=True)
for line in output.splitlines()[1:]:
param, value = line.split(':', 1)
if param.strip() == 'Size':
return int(value.strip())
self.module.fail_json(msg="fatresize failed to provide filesystem size for %s" % dev)
def grow_cmd(self, dev):
cmd = self.module.get_bin_path(self.GROW)
return [cmd, "-s", str(dev.size()), str(dev.path)]
class LVM(Filesystem): class LVM(Filesystem):
MKFS = 'pvcreate' MKFS = 'pvcreate'
MKFS_FORCE_FLAGS = '-f' MKFS_FORCE_FLAGS = '-f'
@ -222,7 +253,7 @@ class LVM(Filesystem):
def get_fs_size(self, dev): def get_fs_size(self, dev):
cmd = self.module.get_bin_path('pvs', required=True) cmd = self.module.get_bin_path('pvs', required=True)
_, size, _ = self.module.run_command([cmd, '--noheadings', '-o', 'pv_size', '--units', 'b', dev], check_rc=True) _, size, _ = self.module.run_command([cmd, '--noheadings', '-o', 'pv_size', '--units', 'b', str(dev)], check_rc=True)
block_count = int(size[:-1]) # block size is 1 block_count = int(size[:-1]) # block size is 1
return block_count return block_count
@ -235,6 +266,7 @@ FILESYSTEMS = {
'reiserfs': Reiserfs, 'reiserfs': Reiserfs,
'xfs': XFS, 'xfs': XFS,
'btrfs': Btrfs, 'btrfs': Btrfs,
'vfat': VFAT,
'LVM2_member': LVM, 'LVM2_member': LVM,
} }
@ -275,6 +307,7 @@ def main():
if not os.path.exists(dev): if not os.path.exists(dev):
module.fail_json(msg="Device %s not found." % dev) module.fail_json(msg="Device %s not found." % dev)
dev = Device(module, dev)
cmd = module.get_bin_path('blkid', required=True) 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)) rc, raw_fs, err = module.run_command("%s -c /dev/null -o value -s TYPE %s" % (cmd, dev))

View file

@ -11,4 +11,5 @@ tested_filesystems:
ext2: {fssize: 10, grow: True} ext2: {fssize: 10, grow: True}
xfs: {fssize: 20, grow: False} # grow requires a mounted filesystem xfs: {fssize: 20, grow: False} # grow requires a mounted filesystem
btrfs: {fssize: 100, grow: False} # grow not implemented btrfs: {fssize: 100, grow: False} # grow not implemented
vfat: {fssize: 20, grow: True}
# untested: lvm, requires a block device # untested: lvm, requires a block device

View file

@ -50,7 +50,7 @@
- name: increase fake device - name: increase fake device
shell: 'dd if=/dev/zero bs=1M count=20 >> {{ dev }}' shell: 'dd if=/dev/zero bs=1M count=20 >> {{ dev }}'
- when: 'grow|bool' - when: 'grow|bool and (fstype != "vfat" or resize_vfat)'
block: block:
- name: Expand filesystem - name: Expand filesystem
filesystem: filesystem:
@ -81,6 +81,7 @@
- 'fs5_result is successful' - 'fs5_result is successful'
- import_tasks: overwrite_another_fs.yml - import_tasks: overwrite_another_fs.yml
when: ansible_system != 'FreeBSD'
always: always:
- file: - file:

View file

@ -1,13 +1,8 @@
- name: 'Recreate "disk" file' - name: 'Recreate "disk" file'
command: 'dd if=/dev/zero of={{ dev }} bs=1M count={{ fssize }}' command: 'dd if=/dev/zero of={{ dev }} bs=1M count={{ fssize }}'
- name: 'Create a vfat filesystem' - name: 'Create a swap filesystem'
command: 'mkfs.vfat {{ dev }}' command: 'mkswap {{ dev }}'
when: ansible_system != 'FreeBSD'
- name: 'Create a vfat filesystem'
command: 'newfs_msdos -F12 {{ dev }}'
when: ansible_system == 'FreeBSD'
- command: 'blkid -c /dev/null -o value -s UUID {{ dev }}' - command: 'blkid -c /dev/null -o value -s UUID {{ dev }}'
register: uuid register: uuid

View file

@ -31,6 +31,20 @@
- btrfsprogs - btrfsprogs
when: ansible_system == 'Linux' when: ansible_system == 'Linux'
- block:
- name: install fatresize
package:
name: fatresize
state: present
- command: fatresize --help
register: fatresize
- set_fact:
fatresize_version: '{{ fatresize.stdout_lines[0] | regex_search("[0-9]+\.[0-9]+\.[0-9]+") }}'
when:
- ansible_system == 'Linux'
- ansible_os_family != 'Suse'
- ansible_os_family != 'RedHat' or (ansible_distribution == 'CentOS' and ansible_distribution_version is version('7.0', '>='))
- command: mke2fs -V - command: mke2fs -V
register: mke2fs register: mke2fs
@ -43,3 +57,5 @@
# Mke2fs no longer complains if the user tries to create a file system # Mke2fs no longer complains if the user tries to create a file system
# using the entire block device. # using the entire block device.
force_creation: "{{ e2fsprogs_version is version('1.43', '<') }}" force_creation: "{{ e2fsprogs_version is version('1.43', '<') }}"
# Earlier versions have a segfault bug
resize_vfat: "{{ fatresize_version|default('0.0') is version('1.0.4', '>=') }}"