Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make PDAdmin calls Idempotent #10

Open
ram-ibm opened this issue May 17, 2017 · 8 comments
Open

Make PDAdmin calls Idempotent #10

ram-ibm opened this issue May 17, 2017 · 8 comments

Comments

@ram-ibm
Copy link
Collaborator

ram-ibm commented May 17, 2017

This is a work in progress. Each PDAdmin command provided will be parsed, the ones that will update will have a corresponding PDADmin command crafted to see if that updated was already done. If updates are already in place then those PDADmin commands will be commented out before execution and the output returned.

@ram-ibm
Copy link
Collaborator Author

ram-ibm commented Mar 7, 2018

FYI - the code developed for this has issues with very large sets of pdadmin commands. Hence put on hold.

@elijah-chan
Copy link

Hi, is there any updates on this? We would love to have idempotent pdadmin calls :)

@ram-ibm
Copy link
Collaborator Author

ram-ibm commented Jan 15, 2020

The code I developed did not scale very well. So this is on hold.

@ram-ibm
Copy link
Collaborator Author

ram-ibm commented May 19, 2020

Here is the code if anyone wants to see if they can come up with a better solution:

import shlex
import logging
import ibmsecurity.utilities.tools
from ibmsecurity.appliance.ibmappliance import IBMError

logger = logging.getLogger(__name__)


def execute(isamAppliance, isamUser, commands, admin_domain='Default', check_mode=False, force=False):
    """
    Execute a pdadmin command
    """
    warnings = []
    logger.debug('User is: ' + isamUser.username)
    if (isinstance(commands, basestring)):
        import ast
        commands = ast.literal_eval(commands)
    update_expected = True
    execute_required = False

    if force is False:
        update_expected, execute_required, commands, warnings = _check(isamAppliance, isamUser, commands, admin_domain,
                                                                       warnings)

    if force is True or execute_required is True:
        if check_mode is True:
            return isamAppliance.create_return_object(changed=update_expected, warnings=warnings)
        else:
            ret_obj = isamAppliance.invoke_post("Execute pdadmin commands", "/isam/pdadmin/",
                                                {
                                                    "admin_id": isamUser.username,
                                                    "admin_pwd": isamUser.password,
                                                    "commands": commands,
                                                    "admin_domain": admin_domain
                                                }, warnings=warnings)
            ret_obj['changed'] = update_expected
            return ret_obj

    return isamAppliance.create_return_object(warnings=warnings, data={})


# NOTE: It is required that no commands are shortened - i.e. "server list" cannot be "s l"

def _check(isamAppliance, isamUser, commands, admin_domain, warnings):
    """
    This function parses each command and checks if an update is going to take place. If so, then those comamnds
    are parsed out and corresponding commands executed to check for idempotency.
    
    :param isamAppliance: 
    :param isamUser: 
    :param commands: 
    :param admin_domain: 
    :return: 
    """
    update_expected = False
    execute_required = False
    l = 0  # Index of commands being processed
    icmds = []  # Array of commands to run for idempotent checks
    icmds_index = []  # Array to keep track of commands to be checked for idempotency
    for cmd in commands:
        c = cmd.strip()
        if c.startswith('#'):
            logger.info("Ignoring line#{0} - detected a comment line.".format(l))
        elif c == '':
            logger.info("Ignoring line#{0} - detected a blank line.".format(l))
        else:
            w = shlex.split(cmd)
            if len(w) > 0:
                e, ec, ic = False, None, None
                if w[0].lower() == "acl":
                    update_expected, execute_required, e, ec, ic, warnings = _check_acl(w, update_expected,
                                                                                        execute_required, warnings)
                elif w[0].lower() == "pop":
                    update_expected, execute_required, e, ec, ic, warnings = _check_pop(w, update_expected,
                                                                                        execute_required, warnings)
                elif w[0].lower() == "object":
                    update_expected, execute_required, e, ec, ic, warnings = _check_object(w, update_expected,
                                                                                           execute_required, warnings)
                elif w[0].lower() == "authzrule":
                    update_expected, execute_required, e, ec, ic, warnings = _check_authzrule(w, update_expected,
                                                                                              execute_required,
                                                                                              warnings)
                elif w[0].lower() == "objectspace":
                    update_expected, execute_required, e, ec, ic, warnings = _check_objectspace(w, update_expected,
                                                                                                execute_required,
                                                                                                warnings)
                if e:
                    logger.error("Error encountered when parsing line#{0}, idempotency check skipped.".format(l))
                    warnings.append("Error encountered when parsing line#{0}, idempotency check skipped.".format(l))
                elif ic is not None:
                    logger.debug("Processing line#{0} of type - {1}".format(l, ec))
                    icmds.append(ic)
                    icmds_index.append({"count": l, "cmd": ec})
        l += 1

    if len(icmds_index) > 0:
        # Ignore any errors while running these commands, it is expected that some comamnds will fail
        try:
            ret_obj = execute(isamAppliance, isamUser, icmds, admin_domain, force=True)
            warnings = warnings + ret_obj['warnings']
        except IBMError as ibmexp:
            # warnings.append(
            #     "Idempotency checks cannot be executed. Typically happens when relevant objects have not been created yet.")
            # return True, True, commands, warnings
            logger.debug(ibmexp)
            ret_obj = isamAppliance.create_return_object()
            ret_obj['data']['result'] = "cmd>" + str(ibmexp).split("cmd>")[1]

        if 'data' in ret_obj and 'result' in ret_obj['data']:
            ocmds = ret_obj['data']['result'].split("cmd> ")
            if len(icmds_index) + 1 != len(ocmds):
                logger.error("Unexpected mis-match in output#{0} versus input commands#{1}.".format(len(icmds_index),
                                                                                                    len(ocmds)))
                warnings.append(
                    "Mis-match in command count run for idempotency check versus the output after execution!")
                return True, True, commands, warnings
            else:
                return _process_cmds(commands, icmds, icmds_index, ocmds, update_expected, execute_required, warnings)

    # Need to return update_expected and execute_required once all commands are parsed
    return True, True, commands, warnings


def _process_cmds(commands, icmds, icmds_index, ocmds, update_expected, execute_required, warnings):
    """
    Compares current setting with the command to be run to see if they are required.
    
    Please see the comments for the actual command being processed. Only commands that will cause changes 
    will be processed. The following are the commands that should have already been marked for execution 
    but will cause no updates.
        acl find <acl-name>
        acl list
        acl list <pattern> <max-return>
        acl show <acl-name>
        acl list <acl-name> attribute
        acl show <acl-name> attribute <attr-name>
    
        pop list <pop-name> attribute
        pop show <pop-name> attribute <attr-name>
        pop list
        pop show <pop-name>
        pop find <pop-name>

        object exists <object-name>
        object list <object-name>
        object list <object-name> attribute
        object listandshow <object-name>
        object show <object-name>
        object show <object-name> attribute <attr-name>
        object access <object-name> <bitgroup>

        authzrule show <authzrule-name>
        authzrule list
        authzrule find <authzrule-name>

        authzrule attach <object-name> <authzrule-name>
        authzrule detach <object-name>
        authzrule create <authzrule-name> <ruletext> [-desc <description>] [-failreason <failreason>]
        authzrule delete <authzrule-name>
        authzrule modify <authzrule-name> description <description>
        authzrule modify <authzrule-name> ruletext <ruletext>
        authzrule modify <authzrule-name> failreason <failreason>
    
        objectspace list

    :param commands: 
    :param icmds: 
    :param icmds_index: 
    :param ocmds: 
    :param update_expected: 
    :param execute_required: 
    :return:
    """
    i = 0
    for ic in icmds:
        # <> # acl create <acl-name>
        if icmds_index[i]['cmd'].startswith("acl create"):
            if "The ACL name specified was not found in the authorization policy database" in ocmds[i + 1]:
                logger.info("ACL was not found, okay to create.")
                update_expected = True
                execute_required = True
            else:
                logger.info("ACL was found, commenting out command.")
                commands[icmds_index[i]['count']] = "# Already Exists: " + commands[icmds_index[i]['count']]
        # <> # acl delete <acl-name>
        elif icmds_index[i]['cmd'].startswith("acl delete"):
            if "The ACL name specified was not found in the authorization policy database" in ocmds[i + 1]:
                logger.info("ACL was not found, commenting out command.")
                commands[icmds_index[i]['count']] = "# Does not Exist: " + commands[icmds_index[i]['count']]
            else:
                logger.info("ACL was found, okay to delete.")
                update_expected = True
                execute_required = True
        # <> # acl attach <object-name> <acl-name>
        elif icmds_index[i]['cmd'].startswith("acl attach"):
            aa = ocmds[i + 1].split(ic)
            aa = aa[1].split('\n')
            cc = commands[icmds_index[i]['count']].split()
            if cc[2] in aa:
                logger.info("ACL already attached to object, commenting out command.")
                commands[icmds_index[i]['count']] = "# Already attached: " + commands[icmds_index[i]['count']]
            else:
                logger.info("ACL not attached to object.")
                update_expected = True
                execute_required = True
        # <> # acl detach <object-name>
        elif icmds_index[i]['cmd'].startswith("acl detach"):
            aa = ocmds[i + 1].split('Attached ACL: ')
            if aa[1].startswith('\n'):
                logger.info("ACL already detached from object, commenting out command.")
                commands[icmds_index[i]['count']] = "# Already detached: " + commands[icmds_index[i]['count']]
            else:
                logger.info("ACL not detached from object.")
                update_expected = True
                execute_required = True
        # <> # acl modify <acl-name> ...
        elif icmds_index[i]['cmd'].startswith("acl modify"):
            cc = shlex.split(commands[icmds_index[i]['count']])
            # <> # acl modify <acl-name> description <description>
            # <> # acl modify <acl-name> set description <description>
            if cc[3].lower() == 'description' or (cc[3].lower() == 'set' and cc[4].lower() == 'description'):
                am = ocmds[i + 1].split('Description: ')
                am = am[1].split('Entries:')
                if cc[3].lower() == "set":
                    cci = 5
                else:
                    cci = 4
                if am[0].strip() == cc[cci]:
                    logger.info("ACL description matches, commenting out command.")
                    commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                else:
                    logger.info("ACL description not set or does not match.")
                    update_expected = True
                    execute_required = True
                    # <> # acl modify <acl-name> remove any-other
                    # <> # acl modify <acl-name> remove group <group-name>
                    # <> # acl modify <acl-name> remove unauthenticated
                    # <> # acl modify <acl-name> remove user <user-name>
            elif cc[3].lower() == 'remove':
                am = ocmds[i + 1].split('Entries: ')
                am = am[1].strip().split('\n')
                entry_matched = False
                for ams in am:
                    ams = ams.strip().lower()
                    amss = ams.split()
                    if (ams == "any-other" and ams == cc[4].lower()) or (
                                    ams == "unauthenticated" and ams == cc[4].lower()) or (
                                    amss[0] == cc[4].lower() and amss[1] == cc[5].lower()):
                        logger.info("ACL entry not removed.")
                        update_expected = True
                        execute_required = True
                        entry_matched = True
                        break
                if entry_matched is False:
                    logger.info("ACL entry removed, commenting out command.")
                    commands[icmds_index[i]['count']] = "# Already removed: " + commands[icmds_index[i]['count']]
                    # <> # acl modify <acl-name> set ...
            elif cc[3].lower() == 'set':
                # <> # acl modify <acl-name> set attribute <attr-name> <attr-value>
                if cc[4].lower() == 'attribute':
                    aa = ocmds[i + 1].split(ic)
                    avs = aa[1].split()
                    if len(avs) < 1:
                        logger.info("ACL attribute/value not set, okay to modify.")
                        update_expected = True
                        execute_required = True
                    elif avs[0] == cc[5] and cc[6] in avs[1:]:
                        logger.info("ACL attribute value already set, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                    else:
                        logger.info("ACL attribute value not found, okay to modify.")
                        update_expected = True
                        execute_required = True
                # <> # acl modify <acl-name> set any-other
                # <> # acl modify <acl-name> set any-other <perms>
                # <> # acl modify <acl-name> set group <group-name>
                # <> # acl modify <acl-name> set group <group-name> <perms>
                # <> # acl modify <acl-name> set unauthenticated
                # <> # acl modify <acl-name> set unauthenticated <perms>
                # <> # acl modify <acl-name> set user <user-name>
                # <> # acl modify <acl-name> set user <user-name> <perms>
                else:
                    am = ocmds[i + 1].split('Entries: ')
                    am = am[1].strip().split('\n')
                    entry_matched = False
                    for ams in am:
                        ams = ams.strip()
                        amss = ams.split()
                        if (ams.lower() == "any-other" and ams.lower() == cc[4].lower()) or (
                                        ams.lower() == "unauthenticated" and ams.lower() == cc[4].lower()) or (
                                        amss[0].lower() == cc[4].lower() and amss[1].lower() == cc[5].lower()):
                            if (len(amss) == 3 and len(cc) == 7 and amss[2] == cc[6]) or (
                                            len(amss) == 2 and len(cc) == 6):
                                logger.info("ACL entry set, commenting out command.")
                                commands[icmds_index[i]['count']] = "# Already set: " + commands[
                                    icmds_index[i]['count']]
                                entry_matched = True
                                break
                    if entry_matched is False:
                        logger.info("ACL entry not set.")
                        update_expected = True
                        execute_required = True
            # <> # acl modify <acl-name> delete attribute ...
            elif cc[3].lower() == 'delete':
                # <> # acl modify <acl-name> delete attribute <attr-name> <attr-value>
                if len(cc) == 7:
                    aa = ocmds[i + 1].split(ic)
                    avs = aa[1].split()
                    if len(avs) < 1:
                        logger.info("ACL attribute/value not set, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already deleted: " + commands[icmds_index[i]['count']]
                    elif avs[0] == cc[5] and cc[6] in avs[1:]:
                        logger.info("ACL attribute value found, okay to delete.")
                        update_expected = True
                        execute_required = True
                    else:
                        logger.info("ACL attribute value not found, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already deleted: " + commands[icmds_index[i]['count']]
                # <> # acl modify <acl-name> delete attribute <attr-name>
                elif len(cc) == 6:
                    aa = ocmds[i + 1].split(ic)
                    avs = aa[1].split()
                    if cc[5] in avs:
                        logger.info("ACL attribute found, okay to delete.")
                        update_expected = True
                        execute_required = True
                    else:
                        logger.info("ACL attribute not found, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already deleted: " + commands[icmds_index[i]['count']]
                else:
                    logger.error("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                        commands[
                                                                                            icmds_index[i]['count']]))
                    update_expected = True
                    execute_required = True
                    warnings.append("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                           commands[
                                                                                               icmds_index[i][
                                                                                                   'count']]))
            else:
                logger.error("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                    commands[icmds_index[i]['count']]))
                update_expected = True
                execute_required = True
                warnings.append("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                       commands[
                                                                                           icmds_index[i]['count']]))
        # <> # pop create <pop-name>
        elif icmds_index[i]['cmd'].startswith("pop create"):
            if "The protected object policy specified was not found" in ocmds[i + 1]:
                logger.info("POP was not found, okay to create.")
                update_expected = True
                execute_required = True
            else:
                logger.info("POP was found, commenting out command.")
                commands[icmds_index[i]['count']] = "# Already Exists: " + commands[icmds_index[i]['count']]
        # <> # pop delete <pop-name>
        elif icmds_index[i]['cmd'].startswith("pop delete"):
            if "The protected object policy specified was not found" in ocmds[i + 1]:
                logger.info("POP was not found, commenting out command.")
                commands[icmds_index[i]['count']] = "# Does not Exist: " + commands[icmds_index[i]['count']]
            else:
                logger.info("POP was found, okay to delete.")
                update_expected = True
                execute_required = True
        # <> # pop attach <object-name> <pop-name>
        elif icmds_index[i]['cmd'].startswith("pop attach"):
            aa = ocmds[i + 1].split(ic)
            aa = aa[1].split('\n')
            cc = commands[icmds_index[i]['count']].split()
            if cc[2] in aa:
                logger.info("POP already attached to object, commenting out command.")
                commands[icmds_index[i]['count']] = "# Already attached: " + commands[icmds_index[i]['count']]
            else:
                logger.info("POP not attached to object.")
                update_expected = True
                execute_required = True
        # <> # pop detach <object-name>
        elif icmds_index[i]['cmd'].startswith("pop detach"):
            aa = ocmds[i + 1].split('Attached POP: ')
            if aa[1].startswith('\n'):
                logger.info("POP already detached from object, commenting out command.")
                commands[icmds_index[i]['count']] = "# Already detached: " + commands[icmds_index[i]['count']]
            else:
                logger.info("POP not detached from object.")
                update_expected = True
                execute_required = True
        # <> # pop modify <acl-name> ...
        elif icmds_index[i]['cmd'].startswith("pop modify"):
            cc = shlex.split(commands[icmds_index[i]['count']])
            if cc[3].lower() == 'set':
                # <> # pop modify <acl-name> set description <description>
                if cc[4].lower() == 'description':
                    am = ocmds[i + 1].split('Description: ')
                    am = am[1].split('Warning:')
                    if am[0].strip() == cc[5]:
                        logger.info("POP description matches, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                    else:
                        logger.info("POP description not set or does not match.")
                        update_expected = True
                        execute_required = True
                # <> # pop modify <pop-name> set qop {none|integrity|privacy}
                elif cc[4].lower() == 'qop':
                    am = ocmds[i + 1].split('Quality of protection: ')
                    am = am[1].split('Time of day access:')
                    if am[0].strip() == cc[5]:
                        logger.info("POP qop matches, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                    else:
                        logger.info("POP qop not set or does not match.")
                        update_expected = True
                        execute_required = True
                # <> # pop modify <pop-name> set warning {yes|no}
                elif cc[4].lower() == 'warning':
                    am = ocmds[i + 1].split('Warning: ')
                    am = am[1].split('Audit level:')
                    if am[0].strip().lower() == cc[5].lower():
                        logger.info("POP warning matches, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                    else:
                        logger.info("POP warning not set or does not match.")
                        update_expected = True
                        execute_required = True
                # <> # pop modify <pop-name> set audit-level {all|none|<audit-level-list>}
                elif cc[4].lower() == 'audit-level':
                    am = ocmds[i + 1].split('Audit level: ')
                    am = am[1].split('Quality of protection:')
                    ams = am[0].strip()
                    ams = ams.strip(',')
                    ams = ams.split(', ')
                    ccs = cc[5].lower().split(',')
                    if ibmsecurity.utilities.tools.json_sort(ams) == ibmsecurity.utilities.tools.json_sort(ccs):
                        logger.info("POP audit-level matches, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                    else:
                        logger.info("POP audit-level not set or does not match.")
                        update_expected = True
                        execute_required = True
                # <> # pop modify <pop-name> set tod-access <{anyday|weekday|<day-list>}>:<{anytime|<time-spec>-<time-spec>}>[:{utc|local}]
                elif cc[4].lower() == 'tod-access':
                    am = ocmds[i + 1].split('Time of day access: ')
                    am = am[1].split('IP Endpoint Authentication Method Policy')
                    ams = am[0].strip().split(':')
                    ams[0] = ams[0].strip(', ')
                    ams[0] = ams[0].split(', ')
                    ccs = cc[5].lower().split(':')
                    ccs[0] = ccs[0].split(',')
                    if ibmsecurity.utilities.tools.json_sort(ams) == ibmsecurity.utilities.tools.json_sort(ccs):
                        logger.info("POP tod-access matches, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                    else:
                        logger.info("POP tod-access not set or does not match.")
                        update_expected = True
                        execute_required = True
                # <> # pop modify <pop-name> set attribute <attr-name> <attr-value>
                elif cc[4].lower() == 'attribute':
                    aa = ocmds[i + 1].split(ic)
                    avs = aa[1].split()
                    if len(avs) < 1:
                        logger.info("POP attribute/value not set, okay to modify.")
                        update_expected = True
                        execute_required = True
                    elif avs[0] == cc[5] and cc[6] in avs[1:]:
                        logger.info("POP attribute value already set, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                    else:
                        logger.info("POP attribute value not found, okay to modify.")
                        update_expected = True
                        execute_required = True
                # pop modify <pop-name> set ipauth remove <network> <netmask>
                # pop modify <pop-name> set ipauth add <network> <netmask> {<auth_level>|forbidden}
                # TODO Need to put in the logic here to handle ipauth
                elif cc[4].lower() == 'ipauth':
                    # pop modify <pop-name> set ipauth anyothernw {<auth_level>|forbidden}
                    if cc[5].lower() == 'anyothernw':
                        aa = ocmds[i + 1].split(ic)
                        avs = aa[1].partition('Auth Level:')
                        avs = avs[2].split('Network: Any Other Network')
                        if avs[0].strip().lower() == cc[6].lower():
                            logger.info("POP ipauth value already set, commenting out command.")
                            commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                        else:
                            logger.info("POP attribute value not found, okay to modify.")
                            update_expected = True
                            execute_required = True
                    elif cc[5].lower() == 'add':
                        aa = ocmds[i + 1].split(ic)
                        avs = aa[1].partition('Any Other Network')
                        avs = avs[2].split()
                        j = 0
                        matched = False
                        logger.debug(avs)
                        for w in avs:
                            if w == "Network:":
                                if avs[j + 1] == cc[6] and avs[j + 3] == cc[7]:
                                    matched = True
                                    break
                            j = j + 1
                        if matched:
                            logger.info("POP ipauth value already set, commenting out command.")
                            commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                        else:
                            logger.info("POP ipauth value not found, okay to modify.")
                            update_expected = True
                            execute_required = True
            # <> # pop modify <pop-name> delete attribute ...
            elif cc[3].lower() == 'delete':
                # <> # pop modify <pop-name> delete attribute <attr-name> <attr-value>
                if len(cc) == 7:
                    aa = ocmds[i + 1].split(ic)
                    avs = aa[1].split()
                    if len(avs) < 1:
                        logger.info("POP attribute/value not set, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already deleted: " + commands[icmds_index[i]['count']]
                    elif avs[0] == cc[5] and cc[6] in avs[1:]:
                        logger.info("POP attribute value found, okay to delete.")
                        update_expected = True
                        execute_required = True
                    else:
                        logger.info("POP attribute value not found, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already deleted: " + commands[icmds_index[i]['count']]
                # <> # pop modify <pop-name> delete attribute <attr-name>
                elif len(cc) == 6:
                    aa = ocmds[i + 1].split(ic)
                    avs = aa[1].split()
                    if cc[5] in avs:
                        logger.info("POP attribute found, okay to delete.")
                        update_expected = True
                        execute_required = True
                    else:
                        logger.info("POP attribute not found, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already deleted: " + commands[icmds_index[i]['count']]
                else:
                    logger.error("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                        commands[
                                                                                            icmds_index[i]['count']]))
                    update_expected = True
                    execute_required = True
                    warnings.append("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                           commands[
                                                                                               icmds_index[i][
                                                                                                   'count']]))
            else:
                logger.error("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                    commands[icmds_index[i]['count']]))
                update_expected = True
                execute_required = True
                warnings.append("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                       commands[
                                                                                           icmds_index[i]['count']]))

        # <> # object create <object-name> <description> <type> ispolicyattachable {yes|no}
        elif icmds_index[i]['cmd'].startswith("object create"):
            oc = ocmds[i + 1].split("Exists: ")
            logger.debug(oc)
            if oc[1] == 'No':
                logger.info("Object was not found, okay to create.")
                update_expected = True
                execute_required = True
            else:
                logger.info("Object was found, commenting out command.")
                commands[icmds_index[i]['count']] = "# Already Exists: " + commands[icmds_index[i]['count']]
        # <> # object delete <object-name>
        elif icmds_index[i]['cmd'].startswith("object delete"):
            oc = ocmds[i + 1].split("Exists: ")
            logger.debug(oc)
            if oc[1] == 'No':
                logger.info("Object was not found, commenting out command.")
                commands[icmds_index[i]['count']] = "# Does not Exist: " + commands[icmds_index[i]['count']]
            else:
                logger.info("Object was found, okay to delete.")
                update_expected = True
                execute_required = True
        # <> # object copy [-recursive] <src-object-name> <dst-object-name>
        elif icmds_index[i]['cmd'].startswith("object copy"):
            oc = ocmds[i + 1].split("Exists: ")
            logger.debug(oc)
            if oc[1] == 'No':
                logger.info("Destination Object was not found, okay to copy.")
                update_expected = True
                execute_required = True
            else:
                logger.info("Destination Object was found, commenting out copy.")
                commands[icmds_index[i]['count']] = "# Already exists: " + commands[icmds_index[i]['count']]
        # <> # object modify <object-name> ...
        elif icmds_index[i]['cmd'].startswith("object modify"):
            cc = shlex.split(commands[icmds_index[i]['count']])
            if cc[3].lower() == 'set':
                # <> # object modify <object-name> set description <description>
                if cc[4].lower() == 'description':
                    am = ocmds[i + 1].split('Description: ')
                    am = am[1].split('Type:')
                    if am[0].strip() == cc[5]:
                        logger.info("Object description matches, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                    else:
                        logger.info("Object description not set or does not match.")
                        update_expected = True
                        execute_required = True
                # <> # object modify <object-name> set type <type>
                elif cc[4].lower() == 'type':
                    am = ocmds[i + 1].split('Type: ')
                    am = am[1].split('Is Policy Attachable:')
                    typ = am[0].split(' ')
                    if typ[0] == cc[5]:
                        logger.info("Object type matches, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                    else:
                        logger.info("Object type not set or does not match.")
                        update_expected = True
                        execute_required = True
                # <> # object modify <object-name> set ispolicyattachable {yes|no}
                elif cc[4].lower() == 'ispolicyattachable':
                    am = ocmds[i + 1].split('Is Policy Attachable: ')
                    am = am[1].split('Extended Attributes:')
                    if am[0].strip().lower() == cc[5].lower():
                        logger.info("Object isPolicyAttachable matches, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                    else:
                        logger.info("Object isPolicyAttachable not set or does not match.")
                        update_expected = True
                        execute_required = True
                # <> # object modify <object-name> set attribute <attr-name> <attr-value>
                elif cc[4].lower() == 'attribute':
                    aa = ocmds[i + 1].split(ic)
                    avs = aa[1].split()
                    if len(avs) < 1:
                        logger.info("Object attribute/value not set, okay to modify.")
                        update_expected = True
                        execute_required = True
                    elif avs[0] == cc[5] and cc[6] in avs[1:]:
                        logger.info("Object attribute value already set, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                    else:
                        logger.info("Object attribute value not found, okay to modify.")
                        update_expected = True
                        execute_required = True
            # <> # object modify <object-name> delete attribute ...
            elif cc[3].lower() == 'delete':
                # <> # object modify <object-name> delete attribute <attr-name> <attr-value>
                if len(cc) == 7:
                    aa = ocmds[i + 1].split(ic)
                    avs = aa[1].split()
                    if len(avs) < 1:
                        logger.info("Object attribute/value not set, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already deleted: " + commands[icmds_index[i]['count']]
                    elif avs[0] == cc[5] and cc[6] in avs[1:]:
                        logger.info("Object attribute value found, okay to delete.")
                        update_expected = True
                        execute_required = True
                    else:
                        logger.info("Object attribute value not found, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already deleted: " + commands[icmds_index[i]['count']]
                # <> # object modify <object-name> delete attribute <attr-name>
                elif len(cc) == 6:
                    aa = ocmds[i + 1].split(ic)
                    avs = aa[1].split()
                    if cc[5] in avs:
                        logger.info("Object attribute found, okay to delete.")
                        update_expected = True
                        execute_required = True
                    else:
                        logger.info("Object attribute not found, commenting out command.")
                        commands[icmds_index[i]['count']] = "# Already deleted: " + commands[icmds_index[i]['count']]
                else:
                    logger.error("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                        commands[
                                                                                            icmds_index[i]['count']]))
                    update_expected = True
                    execute_required = True
                    warnings.append("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                           commands[
                                                                                               icmds_index[i][
                                                                                                   'count']]))
            else:
                logger.error("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                    commands[icmds_index[i]['count']]))
                update_expected = True
                execute_required = True
                warnings.append("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                       commands[
                                                                                           icmds_index[i]['count']]))
        # <> # authzrule create <authzrule-name> <ruletext> [-desc <description>] [-failreason <failreason>]
        elif icmds_index[i]['cmd'].startswith("authzrule create"):
            if "The authorization rule specified was not found" in ocmds[i + 1]:
                logger.info("AuthzRule was not found, okay to create.")
                update_expected = True
                execute_required = True
            else:
                logger.info("AuthzRule was found, commenting out command.")
                commands[icmds_index[i]['count']] = "# Already Exists: " + commands[icmds_index[i]['count']]
        # <> # authzrule delete <authzrule-name>
        elif icmds_index[i]['cmd'].startswith("authzrule delete"):
            if "The authorization rule specified was not found" in ocmds[i + 1]:
                logger.info("AuthzRule was not found, commenting out command.")
                commands[icmds_index[i]['count']] = "# Does not Exist: " + commands[icmds_index[i]['count']]
            else:
                logger.info("AuthzRule was found, okay to delete.")
                update_expected = True
                execute_required = True
        # <> # authzrule attach <object-name> <authzrule-name>
        elif icmds_index[i]['cmd'].startswith("authzrule attach"):
            aa = ocmds[i + 1].split(ic)
            aa = aa[1].split('\n')
            cc = commands[icmds_index[i]['count']].split()
            if cc[2] in aa:
                logger.info("AuthzRule already attached to object, commenting out command.")
                commands[icmds_index[i]['count']] = "# Already attached: " + commands[icmds_index[i]['count']]
            else:
                logger.info("AuthzRule not attached to object.")
                update_expected = True
                execute_required = True
        # <> # authzrule detach <object-name>
        elif icmds_index[i]['cmd'].startswith("authzrule detach"):
            aa = ocmds[i + 1].split('Attached AuthzRule: ')
            if aa[1].startswith('\n'):
                logger.info("AuthzRule already detached from object, commenting out command.")
                commands[icmds_index[i]['count']] = "# Already detached: " + commands[icmds_index[i]['count']]
            else:
                logger.info("AuthzRule not detached from object.")
                update_expected = True
                execute_required = True
        # <> # authzrule modify <authzrule-name> ...
        elif icmds_index[i]['cmd'].startswith("authzrule modify"):
            cc = shlex.split(commands[icmds_index[i]['count']])
            # <> # authzrule modify <authzrule-name> description <description>
            if cc[3].lower() == 'description':
                am = ocmds[i + 1].split('Description: ')
                am = am[1].split('Rule Text:')
                if am[0].strip() == cc[4]:
                    logger.info("AuthzRule description matches, commenting out command.")
                    commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                else:
                    logger.info("AuthzRule description not set or does not match.")
                    update_expected = True
                    execute_required = True
            # <> # authzrule modify <authzrule-name> ruletext <ruletext>
            elif cc[3].lower() == 'ruletext':
                am = ocmds[i + 1].split('Rule Text: ')
                am = am[1].split('Fail Reason:')
                if am[0].strip() == cc[4]:
                    logger.info("AuthzRule ruletext matches, commenting out command.")
                    commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                else:
                    logger.info("AuthzRule ruletext does not match.")
                    update_expected = True
                    execute_required = True
            # <> # authzrule modify <authzrule-name> failreason <failreason>
            elif cc[3].lower() == 'failreason':
                am = ocmds[i + 1].split('Fail Reason: ')
                if am[1].strip().lower() == cc[4]:
                    logger.info("AuthzRule failreason matches, commenting out command.")
                    commands[icmds_index[i]['count']] = "# Already set: " + commands[icmds_index[i]['count']]
                else:
                    logger.info("AuthzRule failreason not set or does not match.")
                    update_expected = True
                    execute_required = True
            else:
                logger.error("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                    commands[icmds_index[i]['count']]))
                update_expected = True
                execute_required = True
                warnings.append("Unexpected command being processed: {0} / {1}".format(icmds_index[i]['cmd'],
                                                                                       commands[
                                                                                           icmds_index[i]['count']]))

                # <> # objectspace create <objectspace-name> <description> <type>
        elif icmds_index[i]['cmd'].startswith("objectspace create"):
            oc = ocmds[i + 1].split("Exists: ")
            logger.debug(oc)
            if oc[1] == 'No':
                logger.info("Object was not found, okay to create.")
                update_expected = True
                execute_required = True
            else:
                logger.info("Object was found, commenting out command.")
                commands[icmds_index[i]['count']] = "# Already Exists: " + commands[icmds_index[i]['count']]
        # <> # objectspace delete <objectspace-name>
        elif icmds_index[i]['cmd'].startswith("objectspace delete"):
            oc = ocmds[i + 1].split("Exists: ")
            logger.debug(oc)
            if oc[1] == 'No':
                logger.info("Object was not found, commenting out command.")
                commands[icmds_index[i]['count']] = "# Does not Exist: " + commands[icmds_index[i]['count']]
            else:
                logger.info("Object was found, okay to delete.")
                update_expected = True
                execute_required = True

        i += 1

    return update_expected, execute_required, commands, warnings


def _check_acl(w, update_expected, execute_required, warnings):
    """
    Parse acl command and return error, detected command to run and/or command to run for idempotent check.
    
    Note: it does not look for errors except when there is a need for idempotency.
    
    :param w: 
    :return e:
    :return 
    """
    if len(w) > 1:
        if w[1].lower() == "show":
            logger.debug("acl show detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "acl show", None, warnings
        elif w[1].lower() == "list":
            logger.debug("acl list detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "acl list", None, warnings
        elif w[1].lower() == "find":
            logger.debug("acl find detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "acl find", None, warnings
        elif w[1].lower() == "attach":
            logger.debug("acl attach detected, need to check if already attached.")
            if len(w) == 4:
                return update_expected, execute_required, False, "acl attach", "acl find {0}".format(w[3]), warnings
            else:
                return update_expected, execute_required, True, "acl attach", None, warnings
        elif w[1].lower() == "create":
            logger.debug("acl create detected, need to check for current existence.")
            if len(w) == 3:
                return update_expected, execute_required, False, "acl create", "acl show {0}".format(w[2]), warnings
            else:
                return update_expected, execute_required, True, "acl create", None, warnings
        elif w[1].lower() == "delete":
            logger.debug("acl delete detected, need to check for current existence.")
            if len(w) == 3:
                return update_expected, execute_required, False, "acl delete", "acl show {0}".format(w[2]), warnings
            else:
                return update_expected, execute_required, True, "acl delete", None, warnings
        elif w[1].lower() == "detach":
            logger.debug("acl detach detected, need to check if acl still attached to object.")
            if len(w) == 3:
                return update_expected, execute_required, False, "acl detach", "object show {0}".format(w[2]), warnings
            else:
                return update_expected, execute_required, True, "acl detach", None, warnings
        elif w[1].lower() == "modify":
            logger.debug("acl modify detected, need to check for current values.")
            if len(w) > 4:
                if w[4].lower() == "attribute":
                    if len(w) == 7:
                        return update_expected, execute_required, False, "acl modify", "acl show {0} attribute {1}".format(
                            w[2], w[5]), warnings
                    else:
                        return update_expected, execute_required, False, "acl modify", "acl list {0} attribute".format(
                            w[2]), warnings
                else:
                    return update_expected, execute_required, False, "acl modify", "acl show {0}".format(w[2]), warnings
            else:
                return update_expected, execute_required, True, "acl modify", None, warnings
        else:
            update_expected = True
            execute_required = True
            logger.error("Unexpected word after acl - {0}".format(w[1]))
            warnings.append("Unexpected word after acl: {0}".format(w[1]))
    else:
        update_expected = True
        execute_required = True
        logger.error("Expecting extra words after acl, none found. Error!")
        warnings.append("Expecting extra words after acl, none found.")

    return update_expected, execute_required, True, "", None, warnings


def _check_pop(w, update_expected, execute_required, warnings):
    """
    Parse pop command and return error, detected command to run and/or command to run for idempotent check.

    Note: it does not look for errors in provided command except when there is a need for idempotency.

    :param w: 
    :return e:
    :return 
    """
    if len(w) > 1:
        if w[1].lower() == "show":
            logger.debug("pop show detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "pop show", None, warnings
        elif w[1].lower() == "list":
            logger.debug("pop list detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "pop list", None, warnings
        elif w[1].lower() == "find":
            logger.debug("pop find detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "pop find", None, warnings
        elif w[1].lower() == "attach":
            logger.debug("pop attach detected, need to check if already attached.")
            if len(w) == 4:
                return update_expected, execute_required, False, "pop attach", "pop find {0}".format(w[3]), warnings
            else:
                return update_expected, execute_required, True, "pop attach", None, warnings
        elif w[1].lower() == "create":
            logger.debug("pop create detected, need to check for current existence.")
            if len(w) == 3:
                return update_expected, execute_required, False, "pop create", "pop show {0}".format(w[2]), warnings
            else:
                return update_expected, execute_required, True, "pop create", None, warnings
        elif w[1].lower() == "delete":
            logger.debug("pop delete detected, need to check for current existence.")
            if len(w) == 3:
                return update_expected, execute_required, False, "pop delete", "pop show {0}".format(w[2]), warnings
            else:
                return update_expected, execute_required, True, "pop delete", None, warnings
        elif w[1].lower() == "detach":
            logger.debug("pop detach detected, need to check if pop still attached to object.")
            if len(w) == 3:
                return update_expected, execute_required, False, "pop detach", "object show {0}".format(w[2]), warnings
            else:
                return update_expected, execute_required, True, "pop detach", None, warnings
        elif w[1].lower() == "modify":
            logger.debug("pop modify detected, need to check for current values.")
            if len(w) > 4:
                if w[4].lower() == "attribute":
                    if len(w) == 7:
                        return update_expected, execute_required, False, "pop modify", "pop show {0} attribute {1}".format(
                            w[2], w[5]), warnings
                    else:
                        return update_expected, execute_required, False, "pop modify", "pop list {0} attribute".format(
                            w[2]), warnings
                else:
                    return update_expected, execute_required, False, "pop modify", "pop show {0}".format(w[2]), warnings
            else:
                return update_expected, execute_required, True, "pop modify", None, warnings
        else:
            update_expected = True
            execute_required = True
            logger.error("Unexpected word after pop - {0}".format(w[1]))
            warnings.append("Unexpected word after pop: {0}".format(w[1]))
    else:
        update_expected = True
        execute_required = True
        logger.error("Expecting extra words after pop, none found. Error!")
        warnings.append("Expecting extra words after pop, none found.")

    return update_expected, execute_required, True, "", None, warnings


def _check_authzrule(w, update_expected, execute_required, warnings):
    """
    Parse authzrule command and return error, detected command to run and/or command to run for idempotent check.

    Note: it does not look for errors in provided command except when there is a need for idempotency.

    :param w: 
    :return e:
    :return 
    """
    if len(w) > 1:
        if w[1].lower() == "show":
            logger.debug("authzrule show detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "authzrule show", None, warnings
        elif w[1].lower() == "list":
            logger.debug("authzrule list detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "authzrule list", None, warnings
        elif w[1].lower() == "find":
            logger.debug("authzrule find detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "authzrule find", None, warnings
        elif w[1].lower() == "attach":
            logger.debug("authzrule attach detected, need to check if already attached.")
            if len(w) == 4:
                return update_expected, execute_required, False, "authzrule attach", "authzrule find {0}".format(
                    w[3]), warnings
            else:
                return update_expected, execute_required, True, "authzrule attach", None, warnings
        elif w[1].lower() == "create":
            logger.debug("authzrule create detected, need to check for current existence.")
            if len(w) == 4 or len(w) == 6 or len(w) == 8:
                return update_expected, execute_required, False, "authzrule create", "authzrule show {0}".format(
                    w[2]), warnings
            else:
                return update_expected, execute_required, True, "authzrule create", None, warnings
        elif w[1].lower() == "delete":
            logger.debug("authzrule delete detected, need to check for current existence.")
            if len(w) == 3:
                return update_expected, execute_required, False, "authzrule delete", "authzrule show {0}".format(
                    w[2]), warnings
            else:
                return update_expected, execute_required, True, "authzrule delete", None, warnings
        elif w[1].lower() == "detach":
            logger.debug("authzrule detach detected, need to check if authzrule still attached to object.")
            if len(w) == 3:
                return update_expected, execute_required, False, "authzrule detach", "object show {0}".format(
                    w[2]), warnings
            else:
                return update_expected, execute_required, True, "authzrule detach", None, warnings
        elif w[1].lower() == "modify":
            logger.debug("authzrule modify detected, need to check for current values.")
            if len(w) == 5:
                return update_expected, execute_required, False, "authzrule modify", "authzrule show {0}".format(
                    w[2]), warnings
            else:
                return update_expected, execute_required, True, "authzrule modify", None, warnings
        else:
            update_expected = True
            execute_required = True
            logger.error("Unexpected word after authzrule - {0}".format(w[1]))
            warnings.append("Unexpected word after authzrule: {0}".format(w[1]))
    else:
        update_expected = True
        execute_required = True
        logger.error("Expecting extra words after authzrule, none found. Error!")
        warnings.append("Expecting extra words after authzrule, none found.")

    return update_expected, execute_required, True, "", None, warnings


def _check_object(w, update_expected, execute_required, warnings):
    """
    Parse object command and return error, detected command to run and/or command to run for idempotent check.

    Note: it does not look for errors in provided command except when there is a need for idempotency.

    :param w: 
    :return e:
    :return 
    """
    if len(w) > 1:
        if w[1].lower() == "exists":
            logger.debug("object exists detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "object exists", None, warnings
        elif w[1].lower() == "list":
            logger.debug("object list detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "object list", None, warnings
        elif w[1].lower() == "listandshow":
            logger.debug("object listandshow detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "object listandshow", None, warnings
        elif w[1].lower() == "show":
            logger.debug("object show detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "object show", None, warnings
        elif w[1].lower() == "access":
            logger.debug("object access detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "object access", None, warnings
        elif w[1].lower() == "create":
            logger.debug("object create detected, need to check for current existence.")
            if len(w) == 7 or len(w) == 5:
                return update_expected, execute_required, False, "object create", "object exists {0}".format(
                    w[2]), warnings
            else:
                return update_expected, execute_required, True, "object create", None, warnings
        elif w[1].lower() == "delete":
            logger.debug("object delete detected, need to check for current existence.")
            if len(w) == 3:
                return update_expected, execute_required, False, "object delete", "object exists {0}".format(
                    w[2]), warnings
            else:
                return update_expected, execute_required, True, "object delete", None, warnings
        elif w[1].lower() == "copy":
            logger.debug("object copy detected, need to check for existence of destination.")
            if len(w) == 5:
                return update_expected, execute_required, False, "object copy", "object exists {0}".format(
                    w[4]), warnings
            elif len(w) == 4:
                return update_expected, execute_required, False, "object copy", "object exists {0}".format(
                    w[3]), warnings
            else:
                return update_expected, execute_required, True, "object copy", None, warnings
        elif w[1].lower() == "modify":
            logger.debug("object modify detected, need to check for current values.")
            if len(w) > 4:
                if w[4].lower() == "attribute":
                    if len(w) == 7:
                        return update_expected, execute_required, False, "object modify", "object show {0} attribute {1}".format(
                            w[2], w[5]), warnings
                    else:
                        return update_expected, execute_required, False, "object modify", "object list {0} attribute".format(
                            w[2]), warnings
                else:
                    return update_expected, execute_required, False, "object modify", "object show {0}".format(
                        w[2]), warnings
            else:
                return update_expected, execute_required, True, "pop modify", None, warnings
        else:
            update_expected = True
            execute_required = True
            logger.error("Unexpected word after object - {0}".format(w[1]))
            warnings.append("Unexpected word after object: {0}".format(w[1]))
    else:
        update_expected = True
        execute_required = True
        logger.error("Expecting extra words after object, none found. Error!")
        warnings.append("Expecting extra words after object, none found.")

    return update_expected, execute_required, True, "", None, warnings


def _check_objectspace(w, update_expected, execute_required, warnings):
    """
    Parse objectspace command and return error, detected command to run and/or command to run for idempotent check.

    Note: it does not look for errors in provided command except when there is a need for idempotency.

    :param w: 
    :return e:
    :return 
    """
    if len(w) > 1:
        if w[1].lower() == "list":
            logger.debug("objectspace list detected, will be unchanged for execution. No changes expected.")
            execute_required = True
            return update_expected, execute_required, False, "objectspace list", None, warnings
        elif w[1].lower() == "create":
            logger.debug("objectspace create detected, need to check for current existence.")
            if len(w) == 5:
                return update_expected, execute_required, False, "objectspace create", "object exists {0}".format(
                    w[2]), warnings
            else:
                return update_expected, execute_required, True, "objectspace create", None, warnings
        elif w[1].lower() == "delete":
            logger.debug("objectspace delete detected, need to check for current existence.")
            if len(w) == 3:
                return update_expected, execute_required, False, "objectspace delete", "object exists {0}".format(
                    w[2]), warnings
            else:
                return update_expected, execute_required, True, "objectspace delete", None, warnings
        else:
            update_expected = True
            execute_required = True
            logger.error("Unexpected word after objectspace - {0}".format(w[1]))
            warnings.append("Unexpected word after objectspace: {0}".format(w[1]))
    else:
        update_expected = True
        execute_required = True
        logger.error("Expecting extra words after objectspace, none found. Error!")
        warnings.append("Expecting extra words after objectspace, none found.")

    return update_expected, execute_required, True, "", None, warnings

@kalemontes
Copy link

Any updates on this issue ?

@ram-ibm
Copy link
Collaborator Author

ram-ibm commented Oct 7, 2020

The code I provided works - but it breaks when there are many thousands of pdadmin commands to be run at a time. Hence has been on the backburner.

@vjt
Copy link

vjt commented Dec 9, 2020

Hi, this is my approach. It doesn't cover all the idempotent requirements, but it is simple enough to be readable and maintainable, and potentially extended to make it all idempotent.

playbooks/isam.yml

- hosts: isam
  roles:
  - lib/ibm/isam
  vars:
    ISAM:
      policy:
      - name: 'POP: forbidden'
        check: pop show forbidden
        set:
        - pop create forbidden
        #
        - pop modify forbidden set description "Never allow access to this object"
        - pop modify forbidden set ipauth      anyothernw forbidden
        - pop modify forbidden set audit-level all
        #
        - pop attach /WebSEAL/example.org/dynurl/forbidden forbidden

      - name: 'POP: anonymous'
        check: pop show anonymous
        set:
        - pop create anonymous
        #
        - pop modify anonymous set description "Anonymous access"
        - pop modify anonymous set ipauth      anyothernw 0
        - pop modify anonymous set audit-level none
        #
        - pop attach /WebSEAL/example.org             anonymous
        - pop attach /WebSEAL/example.org/favicon.ico anonymous
        - pop attach /WebSEAL/example.org/index.html  anonymous

roles/lib/ibm/isam/tasks/main.yml

# ... omissis ...

- include_tasks:
    file: policy.yml
    apply:
      tags: [ isam, isam/policy ]
  loop: '{{ ISAM.policy }}'
  loop_control:
    loop_var: policy
    label: '{{ policy.name }}'
  tags: [ isam, isam/policy ]

roles/lib/ibm/isam/tasks/policy.yml

- name: 'Check policy: {{ policy.name }}'
  isamadmin:
    appliance: "{{ ansible_host }}"
    username:  admin@local
    password:  '{{ ISAM.admin_password }}'
    isamuser:  sec_master
    isampwd:   '{{ ISAM.runtime.admin_pwd }}'
    commands:  [ '{{ policy.check }}' ]
  register: pdadmin_check
  changed_when: false
  failed_when: false

- when: pdadmin_check.msg|default('') is search('Error')
  name: 'Set policy: {{ policy.name }}'
  isamadmin:
    appliance: "{{ ansible_host }}"
    username:  admin@local
    password:  '{{ ISAM.admin_password }}'
    isamuser:  sec_master
    isampwd:   '{{ ISAM.runtime.admin_pwd }}'
    commands:  '{{ policy.set }}'
  register: pdadmin_set
  failed_when: >-
    pdadmin_set.failed or
    pdadmin_set.data.result is search('Error')

Available to further describe, if interested.

@ram-ibm
Copy link
Collaborator Author

ram-ibm commented Dec 10, 2020

I saw this yesterday and read it a couple more times today - I am not sure how it is idempotent, I mean by avoiding a change if it is already in place? Perhaps I need to run the logic to understand it better.

FYI - my code above works, it fails when there are thousands of pdadmin commands to process. So the problem is scale, for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants