#!/usr/bin/python # Copyright (c) 2017, 2018 Kairo Araujo # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later from __future__ import annotations DOCUMENTATION = r""" author: - Kairo Araujo (@kairoaraujo) module: aix_devices short_description: Manages AIX devices description: - This module discovers, defines, removes and modifies attributes of AIX devices. extends_documentation_fragment: - community.general.attributes attributes: check_mode: support: full diff_mode: support: none options: attributes: description: - A list of device attributes. type: dict device: description: - The name of the device. - V(all) is valid to rescan C(available) all devices (AIX C(cfgmgr) command). type: str force: description: - Forces action. type: bool default: false recursive: description: - Removes or defines a device and children devices. type: bool default: false state: description: - Controls the device state. - V(available) (alias V(present)) rescan a specific device or all devices (when O(device) is not specified). - V(removed) (alias V(absent) removes a device. - V(defined) changes device to Defined state. type: str choices: [available, defined, removed] default: available """ EXAMPLES = r""" - name: Scan new devices community.general.aix_devices: device: all state: available - name: Scan new virtual devices (vio0) community.general.aix_devices: device: vio0 state: available - name: Removing IP alias to en0 community.general.aix_devices: device: en0 attributes: delalias4: 10.0.0.100,255.255.255.0 - name: Removes ent2 community.general.aix_devices: device: ent2 state: removed - name: Put device en2 in Defined community.general.aix_devices: device: en2 state: defined - name: Removes ent4 (inexistent). community.general.aix_devices: device: ent4 state: removed - name: Put device en4 in Defined (inexistent) community.general.aix_devices: device: en4 state: defined - name: Put vscsi1 and children devices in Defined state. community.general.aix_devices: device: vscsi1 recursive: true state: defined - name: Removes vscsi1 and children devices. community.general.aix_devices: device: vscsi1 recursive: true state: removed - name: Changes en1 mtu to 9000 and disables arp. community.general.aix_devices: device: en1 attributes: mtu: 900 arp: 'off' state: available - name: Configure IP, netmask and set en1 up. community.general.aix_devices: device: en1 attributes: netaddr: 192.168.0.100 netmask: 255.255.255.0 state: up state: available - name: Adding IP alias to en0 community.general.aix_devices: device: en0 attributes: alias4: 10.0.0.100,255.255.255.0 state: available """ RETURN = r""" # """ from ansible.module_utils.basic import AnsibleModule def _check_device(module, device): """ Check if device already exists and the state. Args: module: Ansible module. device: device to be checked. Returns: bool, device state """ lsdev_cmd = module.get_bin_path("lsdev", True) rc, lsdev_out, err = module.run_command([lsdev_cmd, "-C", "-l", device]) if rc != 0: module.fail_json(msg="Failed to run lsdev", rc=rc, err=err) if lsdev_out: device_state = lsdev_out.split()[1] return True, device_state device_state = None return False, device_state def _check_device_attr(module, device, attr): """ Args: module: Ansible module. device: device to check attributes. attr: attribute to be checked. Returns: """ lsattr_cmd = module.get_bin_path("lsattr", True) rc, lsattr_out, err = module.run_command([lsattr_cmd, "-El", device, "-a", f"{attr}"]) hidden_attrs = ["delalias4", "delalias6"] if rc == 255: if attr in hidden_attrs: current_param = "" else: current_param = None return current_param elif rc != 0: module.fail_json(msg=f"Failed to run lsattr: {err}", rc=rc, err=err) current_param = lsattr_out.split()[1] return current_param def discover_device(module, device): """Discover AIX devices.""" cfgmgr_cmd = module.get_bin_path("cfgmgr", True) if device is not None: device = f"-l {device}" else: device = "" changed = True msg = "" if not module.check_mode: rc, cfgmgr_out, err = module.run_command([cfgmgr_cmd, device]) changed = True msg = cfgmgr_out return changed, msg def change_device_attr(module, attributes, device, force): """Change AIX device attribute.""" attr_changed = [] attr_not_changed = [] attr_invalid = [] chdev_cmd = module.get_bin_path("chdev", True) for attr in list(attributes.keys()): new_param = attributes[attr] current_param = _check_device_attr(module, device, attr) if current_param is None: attr_invalid.append(attr) elif current_param != new_param: if force: cmd = [chdev_cmd, "-l", device, "-a", f"{attr}={attributes[attr]}", f"{force}"] else: cmd = [chdev_cmd, "-l", device, "-a", f"{attr}={attributes[attr]}"] if not module.check_mode: rc, chdev_out, err = module.run_command(cmd) if rc != 0: module.exit_json(msg="Failed to run chdev.", rc=rc, err=err) attr_changed.append(attributes[attr]) else: attr_not_changed.append(attributes[attr]) if len(attr_changed) > 0: changed = True attr_changed_msg = f"Attributes changed: {','.join(attr_changed)}. " else: changed = False attr_changed_msg = "" if len(attr_not_changed) > 0: attr_not_changed_msg = f"Attributes already set: {','.join(attr_not_changed)}. " else: attr_not_changed_msg = "" if len(attr_invalid) > 0: attr_invalid_msg = f"Invalid attributes: {', '.join(attr_invalid)} " else: attr_invalid_msg = "" msg = f"{attr_changed_msg}{attr_not_changed_msg}{attr_invalid_msg}" return changed, msg def remove_device(module, device, force, recursive, state): """Puts device in defined state or removes device.""" state_opt = {"removed": "-d", "absent": "-d", "defined": ""} recursive_opt = {True: "-R", False: ""} recursive = recursive_opt[recursive] state = state_opt[state] changed = True msg = "" rmdev_cmd = module.get_bin_path("rmdev", True) if not module.check_mode: if state: rc, rmdev_out, err = module.run_command([rmdev_cmd, "-l", device, f"{recursive}", f"{force}"]) else: rc, rmdev_out, err = module.run_command([rmdev_cmd, "-l", device, f"{recursive}"]) if rc != 0: module.fail_json(msg="Failed to run rmdev", rc=rc, err=err) msg = rmdev_out return changed, msg def main(): module = AnsibleModule( argument_spec=dict( attributes=dict(type="dict"), device=dict(type="str"), force=dict(type="bool", default=False), recursive=dict(type="bool", default=False), state=dict(type="str", default="available", choices=["available", "defined", "removed"]), ), supports_check_mode=True, ) force_opt = { True: "-f", False: "", } attributes = module.params["attributes"] device = module.params["device"] force = force_opt[module.params["force"]] recursive = module.params["recursive"] state = module.params["state"] result = dict( changed=False, msg="", ) if state == "available" or state == "present": if attributes: # change attributes on device device_status, device_state = _check_device(module, device) if device_status: result["changed"], result["msg"] = change_device_attr(module, attributes, device, force) else: result["msg"] = f"Device {device} does not exist." else: # discovery devices (cfgmgr) if device and device != "all": device_status, device_state = _check_device(module, device) if device_status: # run cfgmgr on specific device result["changed"], result["msg"] = discover_device(module, device) else: result["msg"] = f"Device {device} does not exist." else: result["changed"], result["msg"] = discover_device(module, device) elif state == "removed" or state == "absent" or state == "defined": if not device: result["msg"] = "device is required to removed or defined state." else: # Remove device check_device, device_state = _check_device(module, device) if check_device: if state == "defined" and device_state == "Defined": result["changed"] = False result["msg"] = f"Device {device} already in Defined" else: result["changed"], result["msg"] = remove_device(module, device, force, recursive, state) else: result["msg"] = f"Device {device} does not exist." else: result["msg"] = f"Unexpected state {state}." module.fail_json(**result) module.exit_json(**result) if __name__ == "__main__": main()