From 5fa81a437b4a606826eed625aae48e3711d58537 Mon Sep 17 00:00:00 2001 From: Thomas Woerner <twoerner@redhat.com> Date: Thu, 13 Aug 2020 14:12:11 +0200 Subject: [PATCH] New utils script to generate new modules using templates The script will create the module in plugins/modules, the README, test and playbook files. Usage: new_module [options] <module name> <author name> <author email address> Create new ansible-freeipa module using templates. Options: -m Create module with member support -f Force creation -h Print this help Example: utils/new_module -m permission "My Name" myname@some.email --- utils/new_module | 185 +++++++++++ utils/templates/README-module+member.md.in | 132 ++++++++ utils/templates/README-module.md.in | 96 ++++++ utils/templates/ipamodule+member.py.in | 331 +++++++++++++++++++ utils/templates/ipamodule.py.in | 237 +++++++++++++ utils/templates/module-absent.yml.in | 10 + utils/templates/module-member-absent.yml.in | 12 + utils/templates/module-member-present.yml.in | 11 + utils/templates/module-present.yml.in | 10 + utils/templates/test_module+member.yml.in | 98 ++++++ utils/templates/test_module.yml.in | 60 ++++ 11 files changed, 1182 insertions(+) create mode 100755 utils/new_module create mode 100644 utils/templates/README-module+member.md.in create mode 100644 utils/templates/README-module.md.in create mode 100644 utils/templates/ipamodule+member.py.in create mode 100644 utils/templates/ipamodule.py.in create mode 100644 utils/templates/module-absent.yml.in create mode 100644 utils/templates/module-member-absent.yml.in create mode 100644 utils/templates/module-member-present.yml.in create mode 100644 utils/templates/module-present.yml.in create mode 100644 utils/templates/test_module+member.yml.in create mode 100644 utils/templates/test_module.yml.in diff --git a/utils/new_module b/utils/new_module new file mode 100755 index 00000000..8253e4f4 --- /dev/null +++ b/utils/new_module @@ -0,0 +1,185 @@ +#!/bin/bash +# -*- coding: utf-8 -*- + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Copyright (C) 2020 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + + +prog=`basename $0` +topdir=$(dirname $0) + +function usage() { + cat <<EOF +Usage: $prog [options] <module name> <author name> <author email address> + +Create new ansible-freeipa module using templates. + +Options: + -m Create module with member support + -f Force creation + -h Print this help + +EOF +} + +member=0 +force=0 +while getopts "mfh" arg; do + case $arg in + m) member=1;; + f) force=1;; + h) + usage; + exit 0 + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + exit 1 + ;; + esac +done + +for (( i=0; i<OPTIND-1; i++)); do + shift +done + +if [ ${#@} -ne 3 ]; then + usage; + exit 1 +fi + +name=$1 +author=$2 +email=$3 +year=$(date +"%Y") + +if [ -z "$name" -o -z "$author" -o -z "$email" ]; then + [ -z "$name" ] && echo "ERROR: name is not valid" + [ -z "$author" ] && echo "ERROR: author is not valid" + [ -z "$email" ] && echo "ERROR: email is not valid" + echo + usage; + exit 1; +fi + +if [ -f "plugins/modules/ipa$name.py" ]; then + if [ $force == 0 ]; then + echo "ERROR: The module plugins/modules/ipa$name.py already exists" + exit 0 + else + echo "WARNING: Overwriting module plugins/modules/ipa$name.py" + fi +fi +if [ -f "README-$name.md" ]; then + if [ $force == 0 ]; then + echo "ERROR: The module docs file README-$name.md already exists" + exit 0 + else + echo "WARNING: Overwriting module docs file README-$name.md" + fi +fi +if [ -f "playbooks/$name" ]; then + if [ $force == 0 ]; then + echo "ERROR: playbooks/$name already exists" + exit 0 + else + echo "WARNING: Overwriting playbooks/$name" + fi +fi +if [ -d "playbooks/$name" ]; then + if [ $force == 0 ]; then + echo "ERROR: The playbooks folder playbooks/$name already exists" + exit 0 + else + echo "WARNING: Overwriting playbooks in folder playbooks/$name" + fi +else + if [ -f "playbooks/$name" ]; then + echo "ERROR: playbooks/$name is not a directory" + exit 0 + fi +fi +if [ -d "tests/$name" ]; then + if [ $force == 0 ]; then + echo "ERROR: The tests folder tests/$name already exists" + exit 0 + else + echo "WARNING: Overwriting the tests in folder tests/$name" + fi +else + if [ -f "tests/$name" ]; then + echo "ERROR: tests/$name is not a directory" + exit 0 + fi +fi + +# TEMPLATE function + +function template() { + s=$1 + d=$2 + sed -e "s/\$name/$name/g" \ + -e "s/\${name}/${name}/g" \ + -e "s/\${name^}/${name^}/g" \ + -e "s/\$author/$author/g" \ + -e "s/\$email/$email/" \ + -e "s/\$year/$year/" \ + $topdir/templates/$s > $d +} + +# MODULE + +dest=plugins/modules +mkdir -p $dest + +src=ipamodule.py.in +[ $member == 1 ] && src=ipamodule+member.py.in +template $src $dest/ipa$name.py + +# README + +src=README-module.md.in +[ $member == 1 ] && src=README-module+member.md.in +template $src README-$name.md + +# PLAYBOOKS + +dest=playbooks/$name +mkdir -p $dest + +template module-present.yml.in $dest/$name-present.yml +template module-absent.yml.in $dest/$name-absent.yml + +if [ $member == 1 ]; then + template module-member-present.yml.in $dest/$name-member-present.yml + template module-member-absent.yml.in $dest/$name-member-absent.yml +fi + +# TESTS + +dest=tests/$name +mkdir -p $dest + +src=test_module.yml.in +[ $member == 1 ] && src=test_module+member.yml.in +template $src $dest/test_$name.yml diff --git a/utils/templates/README-module+member.md.in b/utils/templates/README-module+member.md.in new file mode 100644 index 00000000..ff362be6 --- /dev/null +++ b/utils/templates/README-module+member.md.in @@ -0,0 +1,132 @@ +${name^} module +============ + +Description +----------- + +The $name module allows to ensure presence and absence of ${name}s and $name members. + +Features +-------- + +* ${name^} management + + +Supported FreeIPA Versions +-------------------------- + +FreeIPA versions 4.4.0 and up are supported by the ipa$name module. + + +Requirements +------------ + +**Controller** +* Ansible version: 2.8+ + +**Node** +* Supported FreeIPA version (see above) + + +Usage +===== + +Example inventory file + +```ini +[ipaserver] +ipaserver.test.local +``` + + +Example playbook to make sure $name "NAME" is present: + +```yaml +--- +- name: Playbook to manage IPA $name. + hosts: ipaserver + become: yes + + tasks: + - ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + # Add needed parameters here +``` + +Example playbook to make sure $name "NAME" member PARAMETER2 VALUE is present: + +```yaml +--- +- name: Playbook to manage IPA $name PARAMETER2 member. + hosts: ipaserver + become: yes + + tasks: + - ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + PARAMETER2: VALUE + action: member +``` + + +Example playbook to make sure $name "NAME" member PARAMETER2 VALUE is absent: + + +```yaml +--- +- name: Playbook to manage IPA $name PARAMETER2 member. + hosts: ipaserver + become: yes + + tasks: + - ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + PARAMETER2: VALUE + action: member + state: absent +``` + + +Example playbook to make sure $name "NAME" is absent: + +```yaml +--- +- name: Playbook to manage IPA $name. + hosts: ipaserver + become: yes + + tasks: + - ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + state: absent +``` + + +MORE EXAMPLE PLAYBOOKS HERE + + +Variables +--------- + +ipa$name +------- + +Variable | Description | Required +-------- | ----------- | -------- +`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no +`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no +`name` \| `ALIAS` | The list of $name name strings. | yes +`PARAMETER1` \| `API_PARAMETER_NAME` | DESCRIPTION | BOOL +`PARAMETER2` \| `API_PARAMETER_NAME` | DESCRIPTION | BOOL +`action` | Work on $name or member level. It can be on of `member` or `$name` and defaults to `$name`. | no +`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no + + +Authors +======= + +$author diff --git a/utils/templates/README-module.md.in b/utils/templates/README-module.md.in new file mode 100644 index 00000000..b57b0c7f --- /dev/null +++ b/utils/templates/README-module.md.in @@ -0,0 +1,96 @@ +${name^} module +============ + +Description +----------- + +The $name module allows to ensure presence and absence of ${name}s. + +Features +-------- + +* ${name^} management + + +Supported FreeIPA Versions +-------------------------- + +FreeIPA versions 4.4.0 and up are supported by the ipa$name module. + + +Requirements +------------ + +**Controller** +* Ansible version: 2.8+ + +**Node** +* Supported FreeIPA version (see above) + + +Usage +===== + +Example inventory file + +```ini +[ipaserver] +ipaserver.test.local +``` + + +Example playbook to make sure $name "NAME" is present: + +```yaml +--- +- name: Playbook to manage IPA $name. + hosts: ipaserver + become: yes + + tasks: + - ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + # Add needed parameters here +``` + + +Example playbook to make sure $name "NAME" is absent: + +```yaml +--- +- name: Playbook to manage IPA $name. + hosts: ipaserver + become: yes + + tasks: + - ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + state: absent +``` + + +MORE EXAMPLE PLAYBOOKS HERE + + +Variables +--------- + +ipa$name +------- + +Variable | Description | Required +-------- | ----------- | -------- +`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no +`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no +`name` \| `ALIAS` | The list of $name name strings. | yes +`PARAMETER1` \| `API_PARAMETER_NAME` | DESCRIPTION | BOOL +`PARAMETER2` \| `API_PARAMETER_NAME` | DESCRIPTION | BOOL +`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no + + +Authors +======= + +$author diff --git a/utils/templates/ipamodule+member.py.in b/utils/templates/ipamodule+member.py.in new file mode 100644 index 00000000..78197351 --- /dev/null +++ b/utils/templates/ipamodule+member.py.in @@ -0,0 +1,331 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Authors: +# $author <$email> +# +# Copyright (C) $year Red Hat +# see file 'COPYING' for use and warranty information +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +ANSIBLE_METADATA = { + "metadata_version": "1.0", + "supported_by": "community", + "status": ["preview"], +} + +DOCUMENTATION = """ +--- +module: ipa$name +short description: Manage FreeIPA $name +description: Manage FreeIPA $name and $name members +options: + ipaadmin_principal: + description: The admin principal. + default: admin + ipaadmin_password: + description: The admin password. + required: false + name: + description: The list of $name name strings. + required: true + aliases: ["API_PARAMETER_NAME"] + PARAMETER1: + description: DESCRIPTION + required: REQUIRED + aliases: ["API_PARAMETER_NAME"] + PARAMETER2: + description: member DESCRIPTION + required: REQUIRED + aliases: ["API_PARAMETER_NAME"] + action: + description: Work on $name or member level. + choices: ["$name", "member"] + default: $name + required: false + state: + description: The state to ensure. + choices: ["present", "absent"] + default: present + required: true +""" + +EXAMPLES = """ +# Ensure $name NAME is present +- ipa$name: + name: NAME + PARAMETERS + +# Ensure $name "NAME" member PARAMETER2 VALUE is present +- ipa$name: + name: NAME + PARAMETER2: VALUE + action: member + +# Ensure $name "NAME" member PARAMETER2 VALUE is absent +- ipa$name: + name: NAME + PARAMETER2: VALUE + action: member + state: absent + +# Ensure $name NAME is absent +- ipa$name: + name: NAME + state: absent + +# Ensure $name NAME ... +- ipa$name: + name: NAME + CHANGE PARAMETERS +""" + +RETURN = """ +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.ansible_freeipa_module import \ + temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \ + compare_args_ipa, module_params_get, gen_add_del_lists +import six + +if six.PY3: + unicode = str + + +def find_$name(module, name): + """Find if a $name with the given name already exist.""" + try: + _result = api_command(module, "$name_show", name, {"all": True}) + except Exception: # pylint: disable=broad-except + # An exception is raised if $name name is not found. + return None + else: + return _result["result"] + + +def gen_args(PARAMETER1): + _args = {} + if PARAMETER1 is not None: + _args["API_PARAMETER1_NAME"] = PARAMETER1 + return _args + + +def gen_member_args(PARAMETER2): + _args = {} + if PARAMETER2 is not None: + _args["API_PARAMETER2_NAME"] = PARAMETER2 + return _args + + +def main(): + ansible_module = AnsibleModule( + argument_spec=dict( + # general + ipaadmin_principal=dict(type="str", default="admin"), + ipaadmin_password=dict(type="str", required=False, no_log=True), + + name=dict(type="list", aliases=["API_PARAMETER_NAME"], + default=None, required=True), + # present + PARAMETER1=dict(required=False, type='str', + aliases=["API_PARAMETER_NAME"], default=None), + PARAMETER2=dict(required=False, type='list', + aliases=["API_PARAMETER_NAME"], default=None), + action=dict(type="str", default="$name", + choices=["member", "$name"]), + # state + state=dict(type="str", default="present", + choices=["present", "absent"]), + ), + supports_check_mode=True, + ) + + ansible_module._ansible_debug = True + + # Get parameters + + # general + ipaadmin_principal = module_params_get(ansible_module, + "ipaadmin_principal") + ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") + names = module_params_get(ansible_module, "name") + + # present + PARAMETER1 = module_params_get(ansible_module, "PARAMETER1") + PARAMETER2 = module_params_get(ansible_module, "PARAMETER2") + action = module_params_get(ansible_module, "action") + + # state + state = module_params_get(ansible_module, "state") + + # Check parameters + + invalid = [] + + if state == "present": + if len(names) != 1: + ansible_module.fail_json( + msg="Only one $name can be added at a time.") + if action == "member": + invalid = ["PARAMETER1"] + + if state == "absent": + if len(names) < 1: + ansible_module.fail_json(msg="No name given.") + invalid = ["PARAMETER1"] + if action == "$name": + invalid.append("PARAMETER2") + + for x in invalid: + if vars()[x] is not None: + ansible_module.fail_json( + msg="Argument '%s' can not be used with action " + "'%s' and state '%s'" % (x, action, state)) + + # Init + + changed = False + exit_args = {} + ccache_dir = None + ccache_name = None + try: + if not valid_creds(ansible_module, ipaadmin_principal): + ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, + ipaadmin_password) + api_connect() + + commands = [] + for name in names: + # Make sure $name exists + res_find = find_$name(ansible_module, name) + + # Create command + if state == "present": + + # Generate args + args = gen_args(PARAMETER1) + + if action == "$name": + # Found the $name + if res_find is not None: + # For all settings is args, check if there are + # different settings in the find result. + # If yes: modify + if not compare_args_ipa(ansible_module, args, + res_find): + commands.append([name, "$name_mod", args]) + else: + commands.append([name, "$name_add", args]) + + member_args = gen_member_args(PARAMETER2) + if not compare_args_ipa(ansible_module, member_args, + res_find): + + # Generate addition and removal lists + PARAMETER2_add, PARAMETER2_del = gen_add_del_lists( + PARAMETER2, res_find.get("member_PARAMETER2")) + + # Add members + if len(PARAMETER2_add) > 0: + commands.append([name, "$name_add_member", + { + "PARAMETER2": PARAMETER2_add, + }]) + # Remove members + if len(PARAMETER2_del) > 0: + commands.append([name, "$name_remove_member", + { + "PARAMETER2": PARAMETER2_del, + }]) + + elif action == "member": + if res_find is None: + ansible_module.fail_json( + msg="No $name '%s'" % name) + + if PARAMETER2 is None: + ansible_module.fail_json(msg="No PARAMETER2 given") + + commands.append([name, "$name_add_member", + { + "PARAMETER2": PARAMETER2, + }]) + + elif state == "absent": + if action == "$name": + if res_find is not None: + commands.append([name, "$name_del", {}]) + + elif action == "member": + if res_find is None: + ansible_module.fail_json( + msg="No $name '%s'" % name) + + if PARAMETER2 is None: + ansible_module.fail_json(msg="No PARAMETER2 given") + + commands.append([name, "$name_remove_member", + { + "PARAMETER2": PARAMETER2, + }]) + + else: + ansible_module.fail_json(msg="Unkown state '%s'" % state) + + # Execute commands + + for name, command, args in commands: + try: + result = api_command(ansible_module, command, name, + args) + if "completed" in result: + if result["completed"] > 0: + changed = True + else: + changed = True + except Exception as e: + ansible_module.fail_json(msg="%s: %s: %s" % (command, name, + str(e))) + # Get all errors + # All "already a member" and "not a member" failures in the + # result are ignored. All others are reported. + errors = [] + for failed_item in result.get("failed", []): + failed = result["failed"][failed_item] + for member_type in failed: + for member, failure in failed[member_type]: + if "already a member" in failure \ + or "not a member" in failure: + continue + errors.append("%s: %s %s: %s" % ( + command, member_type, member, failure)) + if len(errors) > 0: + ansible_module.fail_json(msg=", ".join(errors)) + + except Exception as e: + ansible_module.fail_json(msg=str(e)) + + finally: + temp_kdestroy(ccache_dir, ccache_name) + + # Done + + ansible_module.exit_json(changed=changed, **exit_args) + + +if __name__ == "__main__": + main() diff --git a/utils/templates/ipamodule.py.in b/utils/templates/ipamodule.py.in new file mode 100644 index 00000000..24243a2f --- /dev/null +++ b/utils/templates/ipamodule.py.in @@ -0,0 +1,237 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Authors: +# $author <$email> +# +# Copyright (C) $year Red Hat +# see file 'COPYING' for use and warranty information +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +ANSIBLE_METADATA = { + "metadata_version": "1.0", + "supported_by": "community", + "status": ["preview"], +} + +DOCUMENTATION = """ +--- +module: ipa$name +short description: Manage FreeIPA $name +description: Manage FreeIPA $name +options: + ipaadmin_principal: + description: The admin principal. + default: admin + ipaadmin_password: + description: The admin password. + required: false + name: + description: The list of $name name strings. + required: true + aliases: ["API_PARAMETER_NAME"] + PARAMETER1: + description: DESCRIPTION + required: REQUIRED + aliases: ["API_PARAMETER_NAME"] + PARAMETER2: + description: DESCRIPTION + required: REQUIRED + aliases: ["API_PARAMETER_NAME"] + state: + description: The state to ensure. + choices: ["present", "absent"] + default: present + required: true +""" + +EXAMPLES = """ +# Ensure $name NAME is present +- ipa$name: + name: NAME + PARAMETERS + +# Ensure $name NAME is absent +- ipa$name: + name: NAME + state: absent + +# Ensure $name NAME ... +- ipa$name: + name: NAME + CHANGE PARAMETERS +""" + +RETURN = """ +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.ansible_freeipa_module import \ + temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \ + compare_args_ipa, module_params_get +import six + +if six.PY3: + unicode = str + + +def find_$name(module, name): + """Find if a $name with the given name already exist.""" + try: + _result = api_command(module, "$name_show", name, {"all": True}) + except Exception: # pylint: disable=broad-except + # An exception is raised if $name name is not found. + return None + else: + return _result["result"] + + +def gen_args(PARAMETER1, PARAMETER2): + _args = {} + if PARAMETER1 is not None: + _args["API_PARAMETER1_NAME"] = PARAMETER1 + if PARAMETER2 is not None: + _args["API_PARAMETER2_NAME"] = PARAMETER2 + return _args + + +def main(): + ansible_module = AnsibleModule( + argument_spec=dict( + # general + ipaadmin_principal=dict(type="str", default="admin"), + ipaadmin_password=dict(type="str", required=False, no_log=True), + + name=dict(type="list", aliases=["API_PARAMETER_NAME"], + default=None, required=True), + # present + PARAMETER1=dict(required=False, type='str', + aliases=["API_PARAMETER_NAME"], default=None), + PARAMETER2=dict(required=False, type='list', + aliases=["API_PARAMETER_NAME"], default=None), + # state + state=dict(type="str", default="present", + choices=["present", "absent"]), + ), + supports_check_mode=True, + ) + + ansible_module._ansible_debug = True + + # Get parameters + + # general + ipaadmin_principal = module_params_get(ansible_module, + "ipaadmin_principal") + ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") + names = module_params_get(ansible_module, "name") + + # present + PARAMETER1 = module_params_get(ansible_module, "PARAMETER1") + PARAMETER2 = module_params_get(ansible_module, "PARAMETER2") + + # state + state = module_params_get(ansible_module, "state") + + # Check parameters + + invalid = [] + + if state == "present": + if len(names) != 1: + ansible_module.fail_json( + msg="Only one $name can be added at a time.") + + if state == "absent": + if len(names) < 1: + ansible_module.fail_json(msg="No name given.") + invalid = ["PARAMETER1", "PARAMETER2"] + + for x in invalid: + if vars()[x] is not None: + ansible_module.fail_json( + msg="Argument '%s' can not be used with state '%s'" % + (x, state)) + + # Init + + changed = False + exit_args = {} + ccache_dir = None + ccache_name = None + try: + if not valid_creds(ansible_module, ipaadmin_principal): + ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, + ipaadmin_password) + api_connect() + + commands = [] + for name in names: + # Make sure $name exists + res_find = find_$name(ansible_module, name) + + # Create command + if state == "present": + + # Generate args + args = gen_args(PARAMETER1, PARAMETER2) + + # Found the $name + if res_find is not None: + # For all settings is args, check if there are + # different settings in the find result. + # If yes: modify + if not compare_args_ipa(ansible_module, args, + res_find): + commands.append([name, "$name_mod", args]) + else: + commands.append([name, "$name_add", args]) + + elif state == "absent": + if res_find is not None: + commands.append([name, "$name_del", {}]) + + else: + ansible_module.fail_json(msg="Unkown state '%s'" % state) + + # Execute commands + + for name, command, args in commands: + try: + result = api_command(ansible_module, command, name, + args) + if "completed" in result: + if result["completed"] > 0: + changed = True + else: + changed = True + except Exception as e: + ansible_module.fail_json(msg="%s: %s: %s" % (command, name, + str(e))) + + except Exception as e: + ansible_module.fail_json(msg=str(e)) + + finally: + temp_kdestroy(ccache_dir, ccache_name) + + # Done + + ansible_module.exit_json(changed=changed, **exit_args) + + +if __name__ == "__main__": + main() diff --git a/utils/templates/module-absent.yml.in b/utils/templates/module-absent.yml.in new file mode 100644 index 00000000..b18a8d01 --- /dev/null +++ b/utils/templates/module-absent.yml.in @@ -0,0 +1,10 @@ +--- +- name: ${name^} absent example + hosts: ipaserver + become: true + + tasks: + - name: Ensure $name NAME is absent + ipa$name: + name: NAME + state: absent diff --git a/utils/templates/module-member-absent.yml.in b/utils/templates/module-member-absent.yml.in new file mode 100644 index 00000000..9ed0ad5d --- /dev/null +++ b/utils/templates/module-member-absent.yml.in @@ -0,0 +1,12 @@ +--- +- name: ${name^} absent example + hosts: ipaserver + become: true + + tasks: + - name: Ensure $name NAME is absent + ipa$name: + name: NAME + PARAMETER2: VALUE + action: member + state: absent diff --git a/utils/templates/module-member-present.yml.in b/utils/templates/module-member-present.yml.in new file mode 100644 index 00000000..3ca5d763 --- /dev/null +++ b/utils/templates/module-member-present.yml.in @@ -0,0 +1,11 @@ +--- +- name: ${name^} member present example + hosts: ipaserver + become: true + + tasks: + - name: Ensure $name NAME is present + ipa$name: + name: NAME + PARAMETER2: VALUE + action: member diff --git a/utils/templates/module-present.yml.in b/utils/templates/module-present.yml.in new file mode 100644 index 00000000..97654933 --- /dev/null +++ b/utils/templates/module-present.yml.in @@ -0,0 +1,10 @@ +--- +- name: ${name^} present example + hosts: ipaserver + become: true + + tasks: + - name: Ensure $name NAME is present + ipa$name: + name: NAME + # Add needed parameters here diff --git a/utils/templates/test_module+member.yml.in b/utils/templates/test_module+member.yml.in new file mode 100644 index 00000000..3191a568 --- /dev/null +++ b/utils/templates/test_module+member.yml.in @@ -0,0 +1,98 @@ +--- +- name: Test $name + hosts: ipaserver + become: true + + tasks: + + # CLEANUP TEST ITEMS + + - name: Ensure $name NAME is absent + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + state: absent + + # CREATE TEST ITEMS + + # TESTS + + - name: Ensure $name NAME is present + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + # Add needed parameters here + register: result + failed_when: not result.changed or result.failed + + - name: Ensure $name NAME is present again + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + # Add needed parameters here + register: result + failed_when: result.changed or result.failed + + - name: Ensure $name NAME member PARAMETER2 VALUE is present + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + PARAMETER2: VALUE + action: member + register: result + failed_when: not result.changed or result.failed + + - name: Ensure $name NAME member PARAMETER2 VALUE is present again + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + PARAMETER2: VALUE + action: member + register: result + failed_when: result.changed or result.failed + + - name: Ensure $name NAME member PARAMETER2 VALUE is absent + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + PARAMETER2: VALUE + action: member + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Ensure $name NAME member PARAMETER2 VALUE is absent again + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + PARAMETER2: VALUE + action: member + state: absent + register: result + failed_when: result.changed or result.failed + + # more tests here + + - name: Ensure $name NAME is absent + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Ensure $name NAME is absent again + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + state: absent + register: result + failed_when: result.changed or result.failed + + # CLEANUP TEST ITEMS + + - name: Ensure $name NAME is absent + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + state: absent diff --git a/utils/templates/test_module.yml.in b/utils/templates/test_module.yml.in new file mode 100644 index 00000000..a565aa72 --- /dev/null +++ b/utils/templates/test_module.yml.in @@ -0,0 +1,60 @@ +--- +- name: Test $name + hosts: ipaserver + become: true + + tasks: + + # CLEANUP TEST ITEMS + + - name: Ensure $name NAME is absent + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + state: absent + + # CREATE TEST ITEMS + + # TESTS + + - name: Ensure $name NAME is present + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + # Add needed parameters here + register: result + failed_when: not result.changed or result.failed + + - name: Ensure $name NAME is present again + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + # Add needed parameters here + register: result + failed_when: result.changed or result.failed + + # more tests here + + - name: Ensure $name NAME is absent + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Ensure $name NAME is absent again + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + state: absent + register: result + failed_when: result.changed or result.failed + + # CLEANUP TEST ITEMS + + - name: Ensure $name NAME is absent + ipa$name: + ipaadmin_password: SomeADMINpassword + name: NAME + state: absent -- GitLab