mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
568 lines
17 KiB
Python
568 lines
17 KiB
Python
|
#!/usr/bin/python
|
||
|
# -*- coding: utf-8 -*-
|
||
|
|
||
|
# Copyright: (c) 2017, Kairo Araujo <kairo@kairo.eti.br>
|
||
|
# 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
|
||
|
|
||
|
DOCUMENTATION = r'''
|
||
|
---
|
||
|
author:
|
||
|
- Kairo Araujo (@kairoaraujo)
|
||
|
module: aix_filesystem
|
||
|
short_description: Configure LVM and NFS file systems for AIX
|
||
|
description:
|
||
|
- This module creates, removes, mount and unmount LVM and NFS file system for
|
||
|
AIX using C(/etc/filesystems).
|
||
|
- For LVM file systems is possible to resize a file system.
|
||
|
options:
|
||
|
account_subsystem:
|
||
|
description:
|
||
|
- Specifies whether the file system is to be processed by the accounting subsystem.
|
||
|
type: bool
|
||
|
default: no
|
||
|
attributes:
|
||
|
description:
|
||
|
- Specifies attributes for files system separated by comma.
|
||
|
type: list
|
||
|
default: agblksize='4096',isnapshot='no'
|
||
|
auto_mount:
|
||
|
description:
|
||
|
- File system is automatically mounted at system restart.
|
||
|
type: bool
|
||
|
default: yes
|
||
|
device:
|
||
|
description:
|
||
|
- Logical volume (LV) device name or remote export device to create a NFS file system.
|
||
|
- It is used to create a file system on an already existing logical volume or the exported NFS file system.
|
||
|
- If not mentioned a new logical volume name will be created following AIX standards (LVM).
|
||
|
type: str
|
||
|
fs_type:
|
||
|
description:
|
||
|
- Specifies the virtual file system type.
|
||
|
type: str
|
||
|
default: jfs2
|
||
|
permissions:
|
||
|
description:
|
||
|
- Set file system permissions. C(rw) (read-write) or C(ro) (read-only).
|
||
|
type: str
|
||
|
choices: [ ro, rw ]
|
||
|
default: rw
|
||
|
mount_group:
|
||
|
description:
|
||
|
- Specifies the mount group.
|
||
|
type: str
|
||
|
filesystem:
|
||
|
description:
|
||
|
- Specifies the mount point, which is the directory where the file system will be mounted.
|
||
|
type: str
|
||
|
required: true
|
||
|
nfs_server:
|
||
|
description:
|
||
|
- Specifies a Network File System (NFS) server.
|
||
|
type: str
|
||
|
rm_mount_point:
|
||
|
description:
|
||
|
- Removes the mount point directory when used with state C(absent).
|
||
|
type: bool
|
||
|
default: no
|
||
|
size:
|
||
|
description:
|
||
|
- Specifies the file system size.
|
||
|
- For already C(present) it will be resized.
|
||
|
- 512-byte blocks, Megabytes or Gigabytes. If the value has M specified
|
||
|
it will be in Megabytes. If the value has G specified it will be in
|
||
|
Gigabytes.
|
||
|
- If no M or G the value will be 512-byte blocks.
|
||
|
- If "+" is specified in begin of value, the value will be added.
|
||
|
- If "-" is specified in begin of value, the value will be removed.
|
||
|
- If "+" or "-" is not specified, the total value will be the specified.
|
||
|
- Size will respects the LVM AIX standards.
|
||
|
type: str
|
||
|
state:
|
||
|
description:
|
||
|
- Controls the file system state.
|
||
|
- C(present) check if file system exists, creates or resize.
|
||
|
- C(absent) removes existing file system if already C(unmounted).
|
||
|
- C(mounted) checks if the file system is mounted or mount the file system.
|
||
|
- C(unmounted) check if the file system is unmounted or unmount the file system.
|
||
|
type: str
|
||
|
required: true
|
||
|
choices: [ absent, mounted, present, unmounted ]
|
||
|
default: present
|
||
|
vg:
|
||
|
description:
|
||
|
- Specifies an existing volume group (VG).
|
||
|
type: str
|
||
|
notes:
|
||
|
- For more C(attributes), please check "crfs" AIX manual.
|
||
|
'''
|
||
|
|
||
|
EXAMPLES = r'''
|
||
|
- name: Create filesystem in a previously defined logical volume.
|
||
|
aix_filesystem:
|
||
|
device: testlv
|
||
|
filesystem: /testfs
|
||
|
state: present
|
||
|
|
||
|
- name: Creating NFS filesystem from nfshost.
|
||
|
aix_filesystem:
|
||
|
device: /home/ftp
|
||
|
nfs_server: nfshost
|
||
|
filesystem: /home/ftp
|
||
|
state: present
|
||
|
|
||
|
- name: Creating a new file system without a previously logical volume.
|
||
|
aix_filesystem:
|
||
|
filesystem: /newfs
|
||
|
size: 1G
|
||
|
state: present
|
||
|
vg: datavg
|
||
|
|
||
|
- name: Unmounting /testfs.
|
||
|
aix_filesystem:
|
||
|
filesystem: /testfs
|
||
|
state: unmounted
|
||
|
|
||
|
- name: Resizing /mksysb to +512M.
|
||
|
aix_filesystem:
|
||
|
filesystem: /mksysb
|
||
|
size: +512M
|
||
|
state: present
|
||
|
|
||
|
- name: Resizing /mksysb to 11G.
|
||
|
aix_filesystem:
|
||
|
filesystem: /mksysb
|
||
|
size: 11G
|
||
|
state: present
|
||
|
|
||
|
- name: Resizing /mksysb to -2G.
|
||
|
aix_filesystem:
|
||
|
filesystem: /mksysb
|
||
|
size: -2G
|
||
|
state: present
|
||
|
|
||
|
- name: Remove NFS filesystem /home/ftp.
|
||
|
aix_filesystem:
|
||
|
filesystem: /home/ftp
|
||
|
rm_mount_point: yes
|
||
|
state: absent
|
||
|
|
||
|
- name: Remove /newfs.
|
||
|
aix_filesystem:
|
||
|
filesystem: /newfs
|
||
|
rm_mount_point: yes
|
||
|
state: absent
|
||
|
'''
|
||
|
|
||
|
RETURN = r'''
|
||
|
changed:
|
||
|
description: Return changed for aix_filesystems actions as true or false.
|
||
|
returned: always
|
||
|
type: bool
|
||
|
msg:
|
||
|
description: Return message regarding the action.
|
||
|
returned: always
|
||
|
type: str
|
||
|
'''
|
||
|
|
||
|
from ansible.module_utils.basic import AnsibleModule
|
||
|
from ansible_collections.ansible.posix.plugins.module_utils.ismount import ismount
|
||
|
import re
|
||
|
|
||
|
|
||
|
def _fs_exists(module, filesystem):
|
||
|
"""
|
||
|
Check if file system already exists on /etc/filesystems.
|
||
|
|
||
|
:param module: Ansible module.
|
||
|
:param filesystem: filesystem name.
|
||
|
:return: True or False.
|
||
|
"""
|
||
|
lsfs_cmd = module.get_bin_path('lsfs', True)
|
||
|
rc, lsfs_out, err = module.run_command("%s -l %s" % (lsfs_cmd, filesystem))
|
||
|
if rc == 1:
|
||
|
if re.findall("No record matching", err):
|
||
|
return False
|
||
|
|
||
|
else:
|
||
|
module.fail_json(msg="Failed to run lsfs. Error message: %s" % err)
|
||
|
|
||
|
else:
|
||
|
|
||
|
return True
|
||
|
|
||
|
|
||
|
def _check_nfs_device(module, nfs_host, device):
|
||
|
"""
|
||
|
Validate if NFS server is exporting the device (remote export).
|
||
|
|
||
|
:param module: Ansible module.
|
||
|
:param nfs_host: nfs_host parameter, NFS server.
|
||
|
:param device: device parameter, remote export.
|
||
|
:return: True or False.
|
||
|
"""
|
||
|
showmount_cmd = module.get_bin_path('showmount', True)
|
||
|
rc, showmount_out, err = module.run_command(
|
||
|
"%s -a %s" % (showmount_cmd, nfs_host))
|
||
|
if rc != 0:
|
||
|
module.fail_json(msg="Failed to run showmount. Error message: %s" % err)
|
||
|
else:
|
||
|
showmount_data = showmount_out.splitlines()
|
||
|
for line in showmount_data:
|
||
|
if line.split(':')[1] == device:
|
||
|
return True
|
||
|
|
||
|
return False
|
||
|
|
||
|
|
||
|
def _validate_vg(module, vg):
|
||
|
"""
|
||
|
Check the current state of volume group.
|
||
|
|
||
|
:param module: Ansible module argument spec.
|
||
|
:param vg: Volume Group name.
|
||
|
:return: True (VG in varyon state) or False (VG in varyoff state) or
|
||
|
None (VG does not exist), message.
|
||
|
"""
|
||
|
lsvg_cmd = module.get_bin_path('lsvg', True)
|
||
|
rc, current_active_vgs, err = module.run_command("%s -o" % lsvg_cmd)
|
||
|
if rc != 0:
|
||
|
module.fail_json(msg="Failed executing %s command." % lsvg_cmd)
|
||
|
|
||
|
rc, current_all_vgs, err = module.run_command("%s" % lsvg_cmd)
|
||
|
if rc != 0:
|
||
|
module.fail_json(msg="Failed executing %s command." % lsvg_cmd)
|
||
|
|
||
|
if vg in current_all_vgs and vg not in current_active_vgs:
|
||
|
msg = "Volume group %s is in varyoff state." % vg
|
||
|
return False, msg
|
||
|
elif vg in current_active_vgs:
|
||
|
msg = "Volume group %s is in varyon state." % vg
|
||
|
return True, msg
|
||
|
else:
|
||
|
msg = "Volume group %s does not exist." % vg
|
||
|
return None, msg
|
||
|
|
||
|
|
||
|
def resize_fs(module, filesystem, size):
|
||
|
""" Resize LVM file system. """
|
||
|
|
||
|
chfs_cmd = module.get_bin_path('chfs', True)
|
||
|
if not module.check_mode:
|
||
|
rc, chfs_out, err = module.run_command('%s -a size="%s" %s' % (chfs_cmd, size, filesystem))
|
||
|
|
||
|
if rc == 28:
|
||
|
changed = False
|
||
|
return changed, chfs_out
|
||
|
elif rc != 0:
|
||
|
if re.findall('Maximum allocation for logical', err):
|
||
|
changed = False
|
||
|
return changed, err
|
||
|
else:
|
||
|
module.fail_json(msg="Failed to run chfs. Error message: %s" % err)
|
||
|
|
||
|
else:
|
||
|
if re.findall('The filesystem size is already', chfs_out):
|
||
|
changed = False
|
||
|
else:
|
||
|
changed = True
|
||
|
|
||
|
return changed, chfs_out
|
||
|
else:
|
||
|
changed = True
|
||
|
msg = ''
|
||
|
|
||
|
return changed, msg
|
||
|
|
||
|
|
||
|
def create_fs(
|
||
|
module, fs_type, filesystem, vg, device, size, mount_group, auto_mount,
|
||
|
account_subsystem, permissions, nfs_server, attributes):
|
||
|
""" Create LVM file system or NFS remote mount point. """
|
||
|
|
||
|
attributes = ' -a '.join(attributes)
|
||
|
|
||
|
# Parameters definition.
|
||
|
account_subsys_opt = {
|
||
|
True: '-t yes',
|
||
|
False: '-t no'
|
||
|
}
|
||
|
|
||
|
if nfs_server is not None:
|
||
|
auto_mount_opt = {
|
||
|
True: '-A',
|
||
|
False: '-a'
|
||
|
}
|
||
|
|
||
|
else:
|
||
|
auto_mount_opt = {
|
||
|
True: '-A yes',
|
||
|
False: '-A no'
|
||
|
}
|
||
|
|
||
|
if size is None:
|
||
|
size = ''
|
||
|
else:
|
||
|
size = "-a size=%s" % size
|
||
|
|
||
|
if device is None:
|
||
|
device = ''
|
||
|
else:
|
||
|
device = "-d %s" % device
|
||
|
|
||
|
if vg is None:
|
||
|
vg = ''
|
||
|
else:
|
||
|
vg_state, msg = _validate_vg(module, vg)
|
||
|
if vg_state:
|
||
|
vg = "-g %s" % vg
|
||
|
else:
|
||
|
changed = False
|
||
|
|
||
|
return changed, msg
|
||
|
|
||
|
if mount_group is None:
|
||
|
mount_group = ''
|
||
|
|
||
|
else:
|
||
|
mount_group = "-u %s" % mount_group
|
||
|
|
||
|
auto_mount = auto_mount_opt[auto_mount]
|
||
|
account_subsystem = account_subsys_opt[account_subsystem]
|
||
|
|
||
|
if nfs_server is not None:
|
||
|
# Creates a NFS file system.
|
||
|
mknfsmnt_cmd = module.get_bin_path('mknfsmnt', True)
|
||
|
if not module.check_mode:
|
||
|
rc, mknfsmnt_out, err = module.run_command('%s -f "%s" %s -h "%s" -t "%s" "%s" -w "bg"' % (
|
||
|
mknfsmnt_cmd, filesystem, device, nfs_server, permissions, auto_mount))
|
||
|
if rc != 0:
|
||
|
module.fail_json(msg="Failed to run mknfsmnt. Error message: %s" % err)
|
||
|
else:
|
||
|
changed = True
|
||
|
msg = "NFS file system %s created." % filesystem
|
||
|
|
||
|
return changed, msg
|
||
|
else:
|
||
|
changed = True
|
||
|
msg = ''
|
||
|
|
||
|
return changed, msg
|
||
|
|
||
|
else:
|
||
|
# Creates a LVM file system.
|
||
|
crfs_cmd = module.get_bin_path('crfs', True)
|
||
|
if not module.check_mode:
|
||
|
cmd = "%s -v %s -m %s %s %s %s %s %s -p %s %s -a %s" % (
|
||
|
crfs_cmd, fs_type, filesystem, vg, device, mount_group, auto_mount, account_subsystem, permissions, size, attributes)
|
||
|
rc, crfs_out, err = module.run_command(cmd)
|
||
|
|
||
|
if rc == 10:
|
||
|
module.exit_json(
|
||
|
msg="Using a existent previously defined logical volume, "
|
||
|
"volume group needs to be empty. %s" % err)
|
||
|
|
||
|
elif rc != 0:
|
||
|
module.fail_json(msg="Failed to run %s. Error message: %s" % (cmd, err))
|
||
|
|
||
|
else:
|
||
|
changed = True
|
||
|
return changed, crfs_out
|
||
|
else:
|
||
|
changed = True
|
||
|
msg = ''
|
||
|
|
||
|
return changed, msg
|
||
|
|
||
|
|
||
|
def remove_fs(module, filesystem, rm_mount_point):
|
||
|
""" Remove an LVM file system or NFS entry. """
|
||
|
|
||
|
# Command parameters.
|
||
|
rm_mount_point_opt = {
|
||
|
True: '-r',
|
||
|
False: ''
|
||
|
}
|
||
|
|
||
|
rm_mount_point = rm_mount_point_opt[rm_mount_point]
|
||
|
|
||
|
rmfs_cmd = module.get_bin_path('rmfs', True)
|
||
|
if not module.check_mode:
|
||
|
cmd = "%s -r %s %s" % (rmfs_cmd, rm_mount_point, filesystem)
|
||
|
rc, rmfs_out, err = module.run_command(cmd)
|
||
|
if rc != 0:
|
||
|
module.fail_json(msg="Failed to run %s. Error message: %s" % (cmd, err))
|
||
|
else:
|
||
|
changed = True
|
||
|
msg = rmfs_out
|
||
|
if not rmfs_out:
|
||
|
msg = "File system %s removed." % filesystem
|
||
|
|
||
|
return changed, msg
|
||
|
else:
|
||
|
changed = True
|
||
|
msg = ''
|
||
|
|
||
|
return changed, msg
|
||
|
|
||
|
|
||
|
def mount_fs(module, filesystem):
|
||
|
""" Mount a file system. """
|
||
|
mount_cmd = module.get_bin_path('mount', True)
|
||
|
|
||
|
if not module.check_mode:
|
||
|
rc, mount_out, err = module.run_command(
|
||
|
"%s %s" % (mount_cmd, filesystem))
|
||
|
if rc != 0:
|
||
|
module.fail_json(msg="Failed to run mount. Error message: %s" % err)
|
||
|
else:
|
||
|
changed = True
|
||
|
msg = "File system %s mounted." % filesystem
|
||
|
|
||
|
return changed, msg
|
||
|
else:
|
||
|
changed = True
|
||
|
msg = ''
|
||
|
|
||
|
return changed, msg
|
||
|
|
||
|
|
||
|
def unmount_fs(module, filesystem):
|
||
|
""" Unmount a file system."""
|
||
|
unmount_cmd = module.get_bin_path('unmount', True)
|
||
|
|
||
|
if not module.check_mode:
|
||
|
rc, unmount_out, err = module.run_command("%s %s" % (unmount_cmd, filesystem))
|
||
|
if rc != 0:
|
||
|
module.fail_json(msg="Failed to run unmount. Error message: %s" % err)
|
||
|
else:
|
||
|
changed = True
|
||
|
msg = "File system %s unmounted." % filesystem
|
||
|
|
||
|
return changed, msg
|
||
|
else:
|
||
|
changed = True
|
||
|
msg = ''
|
||
|
|
||
|
return changed, msg
|
||
|
|
||
|
|
||
|
def main():
|
||
|
module = AnsibleModule(
|
||
|
argument_spec=dict(
|
||
|
account_subsystem=dict(type='bool', default=False),
|
||
|
attributes=dict(type='list', default=["agblksize='4096'", "isnapshot='no'"]),
|
||
|
auto_mount=dict(type='bool', default=True),
|
||
|
device=dict(type='str'),
|
||
|
filesystem=dict(type='str', required=True),
|
||
|
fs_type=dict(type='str', default='jfs2'),
|
||
|
permissions=dict(type='str', default='rw', choices=['rw', 'ro']),
|
||
|
mount_group=dict(type='str'),
|
||
|
nfs_server=dict(type='str'),
|
||
|
rm_mount_point=dict(type='bool', default=False),
|
||
|
size=dict(type='str'),
|
||
|
state=dict(type='str', default='present', choices=['absent', 'mounted', 'present', 'unmounted']),
|
||
|
vg=dict(type='str'),
|
||
|
),
|
||
|
supports_check_mode=True,
|
||
|
)
|
||
|
|
||
|
account_subsystem = module.params['account_subsystem']
|
||
|
attributes = module.params['attributes']
|
||
|
auto_mount = module.params['auto_mount']
|
||
|
device = module.params['device']
|
||
|
fs_type = module.params['fs_type']
|
||
|
permissions = module.params['permissions']
|
||
|
mount_group = module.params['mount_group']
|
||
|
filesystem = module.params['filesystem']
|
||
|
nfs_server = module.params['nfs_server']
|
||
|
rm_mount_point = module.params['rm_mount_point']
|
||
|
size = module.params['size']
|
||
|
state = module.params['state']
|
||
|
vg = module.params['vg']
|
||
|
|
||
|
result = dict(
|
||
|
changed=False,
|
||
|
msg='',
|
||
|
)
|
||
|
|
||
|
if state == 'present':
|
||
|
fs_mounted = ismount(filesystem)
|
||
|
fs_exists = _fs_exists(module, filesystem)
|
||
|
|
||
|
# Check if fs is mounted or exists.
|
||
|
if fs_mounted or fs_exists:
|
||
|
result['msg'] = "File system %s already exists." % filesystem
|
||
|
result['changed'] = False
|
||
|
|
||
|
# If parameter size was passed, resize fs.
|
||
|
if size is not None:
|
||
|
result['changed'], result['msg'] = resize_fs(module, filesystem, size)
|
||
|
|
||
|
# If fs doesn't exist, create it.
|
||
|
else:
|
||
|
# Check if fs will be a NFS device.
|
||
|
if nfs_server is not None:
|
||
|
if device is None:
|
||
|
result['msg'] = 'Parameter "device" is required when "nfs_server" is defined.'
|
||
|
module.fail_json(**result)
|
||
|
else:
|
||
|
# Create a fs from NFS export.
|
||
|
if _check_nfs_device(module, nfs_server, device):
|
||
|
result['changed'], result['msg'] = create_fs(
|
||
|
module, fs_type, filesystem, vg, device, size, mount_group, auto_mount, account_subsystem, permissions, nfs_server, attributes)
|
||
|
|
||
|
if device is None:
|
||
|
if vg is None:
|
||
|
result['msg'] = 'Required parameter "device" and/or "vg" is missing for filesystem creation.'
|
||
|
module.fail_json(**result)
|
||
|
else:
|
||
|
# Create a fs from
|
||
|
result['changed'], result['msg'] = create_fs(
|
||
|
module, fs_type, filesystem, vg, device, size, mount_group, auto_mount, account_subsystem, permissions, nfs_server, attributes)
|
||
|
|
||
|
if device is not None and nfs_server is None:
|
||
|
# Create a fs from a previously lv device.
|
||
|
result['changed'], result['msg'] = create_fs(
|
||
|
module, fs_type, filesystem, vg, device, size, mount_group, auto_mount, account_subsystem, permissions, nfs_server, attributes)
|
||
|
|
||
|
elif state == 'absent':
|
||
|
if ismount(filesystem):
|
||
|
result['msg'] = "File system %s mounted." % filesystem
|
||
|
|
||
|
else:
|
||
|
fs_status = _fs_exists(module, filesystem)
|
||
|
if not fs_status:
|
||
|
result['msg'] = "File system %s does not exist." % filesystem
|
||
|
else:
|
||
|
result['changed'], result['msg'] = remove_fs(module, filesystem, rm_mount_point)
|
||
|
|
||
|
elif state == 'mounted':
|
||
|
if ismount(filesystem):
|
||
|
result['changed'] = False
|
||
|
result['msg'] = "File system %s already mounted." % filesystem
|
||
|
else:
|
||
|
result['changed'], result['msg'] = mount_fs(module, filesystem)
|
||
|
|
||
|
elif state == 'unmounted':
|
||
|
if not ismount(filesystem):
|
||
|
result['changed'] = False
|
||
|
result['msg'] = "File system %s already unmounted." % filesystem
|
||
|
else:
|
||
|
result['changed'], result['msg'] = unmount_fs(module, filesystem)
|
||
|
|
||
|
else:
|
||
|
# Unreachable codeblock
|
||
|
result['msg'] = "Unexpected state %s." % state
|
||
|
module.fail_json(**result)
|
||
|
|
||
|
module.exit_json(**result)
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|