# Buggy code
def main():
    result = {}
    module = AnsibleModule(
        argument_spec=dict(
            category=dict(required=True),
            command=dict(required=True, type="list"),
            baseuri=dict(required=True),
            username=dict(required=True),
            password=dict(required=True, no_log=True),
            id=dict(),
            new_username=dict(),
            new_password=dict(no_log=True),
            roleid=dict(),
            bootdevice=dict(),
            timeout=dict(type="int", default=10),
        ),
        supports_check_mode=False,
    )

    category = module.params["category"]
    command_list = module.params["command"]

    # admin credentials used for authentication
    creds = {"user": module.params["username"], "pswd": module.params["password"]}

    # user to add/modify/delete
    user = {
        "userid": module.params["id"],
        "username": module.params["new_username"],
        "userpswd": module.params["new_password"],
        "userrole": module.params["roleid"],
    }

    # timeout
    timeout = module.params["timeout"]

    # Build root URI
    root_uri = "https://" + module.params["baseuri"]
    rf_uri = "/redfish/v1/"
    rf_utils = RedfishUtils(creds, root_uri, timeout)

    # Check that Category is valid
    if category not in CATEGORY_COMMANDS_ALL:
        module.fail_json(
            msg=to_native(
                "Invalid Category '%s'. Valid Categories = %s"
                % (category, CATEGORY_COMMANDS_ALL.keys())
            )
        )

    # Check that all commands are valid
    for cmd in command_list:
        # Fail if even one command given is invalid
        if cmd not in CATEGORY_COMMANDS_ALL[category]:
            module.fail_json(
                msg=to_native(
                    "Invalid Command '%s'. Valid Commands = %s"
                    % (cmd, CATEGORY_COMMANDS_ALL[category])
                )
            )

    # Organize by Categories / Commands
    if category == "Accounts":
        ACCOUNTS_COMMANDS = {
            "AddUser": rf_utils.add_user,
            "EnableUser": rf_utils.enable_user,
            "DeleteUser": rf_utils.delete_user,
            "DisableUser": rf_utils.disable_user,
            "UpdateUserRole": rf_utils.update_user_role,
            "UpdateUserPassword": rf_utils.update_user_password,
        }

        # execute only if we find an Account service resource
        result = rf_utils._find_accountservice_resource(rf_uri)
        if result["ret"] is False:
            module.fail_json(msg=to_native(result["msg"]))

        for command in command_list:
            result = ACCOUNTS_COMMANDS[command](user)

    elif category == "Systems":
        # execute only if we find a System resource
        result = rf_utils._find_systems_resource(rf_uri)
        if result["ret"] is False:
            module.fail_json(msg=to_native(result["msg"]))

        for command in command_list:
            if "Power" in command:
                result = rf_utils.manage_system_power(command)
            elif command == "SetOneTimeBoot":
                result = rf_utils.set_one_time_boot_device(module.params["bootdevice"])

    elif category == "Chassis":
        result = rf_utils._find_chassis_resource(rf_uri)
        if result["ret"] is False:
            module.fail_json(msg=to_native(result["msg"]))

        led_commands = ["IndicatorLedOn", "IndicatorLedOff", "IndicatorLedBlink"]

        # Check if more than one led_command is present
        num_led_commands = sum([command in led_commands for command in command_list])
        if num_led_commands > 1:
            result = {
                "ret": False,
                "msg": "Only one IndicatorLed command should be sent at a time.",
            }
        else:
            for command in command_list:
                if command in led_commands:
                    result = rf_utils.manage_indicator_led(command)

    elif category == "Manager":
        MANAGER_COMMANDS = {
            "GracefulRestart": rf_utils.restart_manager_gracefully,
            "ClearLogs": rf_utils.clear_logs,
        }

        # execute only if we find a Manager service resource
        result = rf_utils._find_managers_resource(rf_uri)
        if result["ret"] is False:
            module.fail_json(msg=to_native(result["msg"]))

        for command in command_list:
            result = MANAGER_COMMANDS[command]()

    # Return data back or fail with proper message
    if result["ret"] is True:
        del result["ret"]
        module.exit_json(changed=True, msg="Action was successful")
    else:
        module.fail_json(msg=to_native(result["msg"]))


# Traceback (KeyError)
$ bin/ansible-playbook ../test_redfish_command.yml  -vvvv
ansible-playbook 2.8.0.dev0
config file = None
configured module search path = [u'/home/xander/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /mnt/c/Users/amadsen/Coding/lenovo-redfish-automated-testing/ansible/lib/ansible
executable location = bin/ansible-playbook
python version = 2.7.12 (default, Nov 12 2018, 14:36:49) [GCC 5.4.0 20160609]
No config file found; using defaults
setting up inventory plugins
host_list declined parsing /etc/ansible/hosts as it did not pass it's verify_file() method
Skipping due to inventory source not existing or not being readable by the current user
script declined parsing /etc/ansible/hosts as it did not pass it's verify_file() method
auto declined parsing /etc/ansible/hosts as it did not pass it's verify_file() method
Skipping due to inventory source not existing or not being readable by the current user
yaml declined parsing /etc/ansible/hosts as it did not pass it's verify_file() method
Skipping due to inventory source not existing or not being readable by the current user
ini declined parsing /etc/ansible/hosts as it did not pass it's verify_file() method
Skipping due to inventory source not existing or not being readable by the current user
toml declined parsing /etc/ansible/hosts as it did not pass it's verify_file() method
[WARNING]: No inventory was parsed, only implicit localhost is available

[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does
not match 'all'

Loading callback plugin default of type stdout, v2.0 from /mnt/c/Users/amadsen/Coding/lenovo-redfish-automated-testing/ansible/lib/ansible/plugins/callback/default.pyc

PLAYBOOK: test_redfish_command.yml ***************************************************************************Positional arguments: ../test_redfish_command.yml
become_user: root
become_method: sudo
inventory: (u'/etc/ansible/hosts',)
tags: (u'all',)
forks: 5
verbosity: 4
connection: smart
timeout: 10
1 plays in ../test_redfish_command.yml

PLAY [localhost] *********************************************************************************************META: ran handlers

TASK [Set one time boot option] ******************************************************************************task path: /mnt/c/Users/amadsen/Coding/lenovo-redfish-automated-testing/test_redfish_command.yml:9
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: xander
<127.0.0.1> EXEC /bin/sh -c 'echo ~xander &amp;&amp; sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 &amp;&amp; mkdir -p "` echo /home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588 `" &amp;&amp; echo ansible-tmp-1549463086.08-258158793933588="` echo /home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588 `" ) &amp;&amp; sleep 0'
Using module file /mnt/c/Users/amadsen/Coding/lenovo-redfish-automated-testing/ansible/lib/ansible/modules/remote_management/redfish/redfish_command.py
<127.0.0.1> PUT /home/xander/.ansible/tmp/ansible-local-1602jVNR9M/tmpEuc2cS TO /home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588/AnsiballZ_redfish_command.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588/ /home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588/AnsiballZ_redfish_command.py &amp;&amp; sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python /home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588/AnsiballZ_redfish_command.py &amp;&amp; sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'rm -f -r /home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588/ > /dev/null 2>&amp;1 &amp;&amp; sleep 0'
The full traceback is:
Traceback (most recent call last):
File "/home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588/AnsiballZ_redfish_command.py", line 113, in <module>
_ansiballz_main()
File "/home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588/AnsiballZ_redfish_command.py", line 105, in _ansiballz_main
invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
File "/home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588/AnsiballZ_redfish_command.py", line 48, in invoke_module
imp.load_module('__main__', mod, module, MOD_DESC)
File "/tmp/ansible_redfish_command_payload_yaYKzO/__main__.py", line 256, in <module>
File "/tmp/ansible_redfish_command_payload_yaYKzO/__main__.py", line 231, in main
File "/tmp/ansible_redfish_command_payload_yaYKzO/ansible_redfish_command_payload.zip/ansible/module_utils/redfish_utils.py", line 679, in set_one_time_boot_device
KeyError: 'BootMode'

fatal: [localhost]: FAILED! => {
"changed": false,
"module_stderr": "Traceback (most recent call last):\n  File \"/home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588/AnsiballZ_redfish_command.py\", line 113, in <module>\n    _ansiballz_main()\n  File
\"/home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588/AnsiballZ_redfish_command.py\", line 105, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/home/xander/.ansible/tmp/ansible-tmp-1549463086.08-258158793933588/AnsiballZ_redfish_command.py\", line 48, in invoke_module\n
imp.load_module('__main__', mod, module, MOD_DESC)\n  File \"/tmp/ansible_redfish_command_payload_yaYKzO/__main__.py\", line 256, in <module>\n  File \"/tmp/ansible_redfish_command_payload_yaYKzO/__main__.py\", line 231, in main\n  File \"/tmp/ansible_redfish_command_payload_yaYKzO/ansible_redfish_command_payload.zip/ansible/module_utils/redfish_utils.py\", line 679, in set_one_time_boot_device\nKeyError: 'BootMode'\n",
"module_stdout": "",
"msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
"rc": 1
}
to retry, use: --limit @/mnt/c/Users/amadsen/Coding/lenovo-redfish-automated-testing/test_redfish_command.retry

PLAY RECAP ***************************************************************************************************localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0

# Fixed code
def main():
    result = {}
    module = AnsibleModule(
        argument_spec=dict(
            category=dict(required=True),
            command=dict(required=True, type="list"),
            baseuri=dict(required=True),
            username=dict(required=True),
            password=dict(required=True, no_log=True),
            id=dict(),
            new_username=dict(),
            new_password=dict(no_log=True),
            roleid=dict(),
            bootdevice=dict(),
            timeout=dict(type="int", default=10),
            uefi_target=dict(),
            boot_next=dict(),
        ),
        supports_check_mode=False,
    )

    category = module.params["category"]
    command_list = module.params["command"]

    # admin credentials used for authentication
    creds = {"user": module.params["username"], "pswd": module.params["password"]}

    # user to add/modify/delete
    user = {
        "userid": module.params["id"],
        "username": module.params["new_username"],
        "userpswd": module.params["new_password"],
        "userrole": module.params["roleid"],
    }

    # timeout
    timeout = module.params["timeout"]

    # Build root URI
    root_uri = "https://" + module.params["baseuri"]
    rf_uri = "/redfish/v1/"
    rf_utils = RedfishUtils(creds, root_uri, timeout)

    # Check that Category is valid
    if category not in CATEGORY_COMMANDS_ALL:
        module.fail_json(
            msg=to_native(
                "Invalid Category '%s'. Valid Categories = %s"
                % (category, CATEGORY_COMMANDS_ALL.keys())
            )
        )

    # Check that all commands are valid
    for cmd in command_list:
        # Fail if even one command given is invalid
        if cmd not in CATEGORY_COMMANDS_ALL[category]:
            module.fail_json(
                msg=to_native(
                    "Invalid Command '%s'. Valid Commands = %s"
                    % (cmd, CATEGORY_COMMANDS_ALL[category])
                )
            )

    # Organize by Categories / Commands
    if category == "Accounts":
        ACCOUNTS_COMMANDS = {
            "AddUser": rf_utils.add_user,
            "EnableUser": rf_utils.enable_user,
            "DeleteUser": rf_utils.delete_user,
            "DisableUser": rf_utils.disable_user,
            "UpdateUserRole": rf_utils.update_user_role,
            "UpdateUserPassword": rf_utils.update_user_password,
        }

        # execute only if we find an Account service resource
        result = rf_utils._find_accountservice_resource(rf_uri)
        if result["ret"] is False:
            module.fail_json(msg=to_native(result["msg"]))

        for command in command_list:
            result = ACCOUNTS_COMMANDS[command](user)

    elif category == "Systems":
        # execute only if we find a System resource
        result = rf_utils._find_systems_resource(rf_uri)
        if result["ret"] is False:
            module.fail_json(msg=to_native(result["msg"]))

        for command in command_list:
            if "Power" in command:
                result = rf_utils.manage_system_power(command)
            elif command == "SetOneTimeBoot":
                result = rf_utils.set_one_time_boot_device(
                    module.params["bootdevice"],
                    module.params["uefi_target"],
                    module.params["boot_next"],
                )

    elif category == "Chassis":
        result = rf_utils._find_chassis_resource(rf_uri)
        if result["ret"] is False:
            module.fail_json(msg=to_native(result["msg"]))

        led_commands = ["IndicatorLedOn", "IndicatorLedOff", "IndicatorLedBlink"]

        # Check if more than one led_command is present
        num_led_commands = sum([command in led_commands for command in command_list])
        if num_led_commands > 1:
            result = {
                "ret": False,
                "msg": "Only one IndicatorLed command should be sent at a time.",
            }
        else:
            for command in command_list:
                if command in led_commands:
                    result = rf_utils.manage_indicator_led(command)

    elif category == "Manager":
        MANAGER_COMMANDS = {
            "GracefulRestart": rf_utils.restart_manager_gracefully,
            "ClearLogs": rf_utils.clear_logs,
        }

        # execute only if we find a Manager service resource
        result = rf_utils._find_managers_resource(rf_uri)
        if result["ret"] is False:
            module.fail_json(msg=to_native(result["msg"]))

        for command in command_list:
            result = MANAGER_COMMANDS[command]()

    # Return data back or fail with proper message
    if result["ret"] is True:
        del result["ret"]
        changed = result.get("changed", True)
        module.exit_json(changed=changed, msg="Action was successful")
    else:
        module.fail_json(msg=to_native(result["msg"]))