Ejemplo n.º 1
0
def main():
    """entry point for module execution"""
    argument_spec = dict(
        path=dict(required=True),
        content=dict(choices=["config", "nonconfig", "all"]),
        output=dict(choices=["json", "xml"], default="json"),
    )

    module = AnsibleModule(argument_spec=argument_spec,
                           supports_check_mode=True)

    result = {"changed": False}

    try:
        response = restconf.get(module, **module.params)
    except ConnectionError as exc:
        module.fail_json(msg=to_text(exc), code=exc.code)

    if module.params["output"] == "xml":
        try:
            response = dict_to_xml(response)
        except Exception as exc:
            module.fail_json(msg=to_text(exc))

    result.update({"response": response})

    module.exit_json(**result)
Ejemplo n.º 2
0
def main():
    """entry point for module execution
    """
    argument_spec = dict(
        source=dict(choices=["running", "candidate", "startup"]),
        filter=dict(type="raw"),
        display=dict(choices=["json", "pretty", "xml", "native"]),
        lock=dict(
            default="never", choices=["never", "always", "if-supported"]
        ),
    )

    module = AnsibleModule(
        argument_spec=argument_spec, supports_check_mode=True
    )

    capabilities = get_capabilities(module)
    operations = capabilities["device_operations"]

    source = module.params["source"]
    filter = module.params["filter"]

    try:
        filter_data, filter_type = validate_and_normalize_data(filter)
    except Exception as exc:
        module.fail_json(msg=to_text(exc))

    if filter_type == "xml":
        filter_type = "subtree"
    elif filter_type == "json":
        try:
            filter = dict_to_xml(filter_data)
        except Exception as exc:
            module.fail_json(msg=to_text(exc))
        filter_type = "subtree"
    elif filter_type == "xpath":
        pass
    elif filter_type is None:
        if filter_data is not None:
            # to maintain backward compatibility for ansible 2.9 which
            # defaults to "subtree" filter type
            filter_type = "subtree"
            module.warn(
                "The data format of filter option value couldn't be identified, hence set to 'subtree'"
            )
        else:
            pass
    elif filter_type:
        module.fail_json(
            msg="Invalid filter type detected %s for filter value %s"
            % (filter_type, filter)
        )

    lock = module.params["lock"]
    display = module.params["display"]

    if source == "candidate" and not operations.get("supports_commit", False):
        module.fail_json(
            msg="candidate source is not supported on this device"
        )

    if source == "startup" and not operations.get("supports_startup", False):
        module.fail_json(msg="startup source is not supported on this device")

    if filter_type == "xpath" and not operations.get("supports_xpath", False):
        module.fail_json(
            msg="filter value '%s' of type xpath is not supported on this device"
            % filter
        )

    # If source is None, NETCONF <get> operation is issued, reading config/state data
    # from the running datastore. The python expression "(source or 'running')" results
    # in the value of source (if not None) or the value 'running' (if source is None).

    if lock == "never":
        execute_lock = False
    elif (source or "running") in operations.get("lock_datastore", []):
        # lock is requested (always/if-support) and supported => lets do it
        execute_lock = True
    else:
        # lock is requested (always/if-supported) but not supported => issue warning
        module.warn(
            "lock operation on '%s' source is not supported on this device"
            % (source or "running")
        )
        execute_lock = lock == "always"

    if display == "json" and not HAS_JXMLEASE:
        module.fail_json(
            msg="jxmlease is required to display response in json format"
            "but does not appear to be installed. "
            "It can be installed using `pip install jxmlease`"
        )

    filter_spec = (filter_type, filter) if filter_type else None

    if source is not None:
        response = get_config(module, source, filter_spec, execute_lock)
    else:
        response = get(module, filter_spec, execute_lock)

    xml_resp = to_text(tostring(response))
    output = None

    if display == "xml":
        output = remove_namespaces(xml_resp)
    elif display == "json":
        try:
            output = jxmlease.parse(xml_resp)
        except Exception:
            raise ValueError(xml_resp)
    elif display == "pretty":
        output = to_text(tostring(response, pretty_print=True))
    elif display == "native":
        try:
            output = xml_to_dict(xml_resp)
        except Exception as exc:
            module.fail_json(msg=to_text(exc))

    result = {"stdout": xml_resp, "output": output}

    module.exit_json(**result)
Ejemplo n.º 3
0
def main():
    """ main entry point for module execution
    """
    backup_spec = dict(filename=dict(), dir_path=dict(type="path"))
    argument_spec = dict(
        content=dict(aliases=["xml"], type="raw"),
        target=dict(
            choices=["auto", "candidate", "running"],
            default="auto",
            aliases=["datastore"],
        ),
        source_datastore=dict(aliases=["source"]),
        format=dict(choices=["xml", "text", "json"]),
        lock=dict(
            choices=["never", "always", "if-supported"], default="always"
        ),
        default_operation=dict(choices=["merge", "replace", "none"]),
        confirm=dict(type="int", default=0),
        confirm_commit=dict(type="bool", default=False),
        error_option=dict(
            choices=[
                "stop-on-error",
                "continue-on-error",
                "rollback-on-error",
            ],
            default="stop-on-error",
        ),
        backup=dict(type="bool", default=False),
        backup_options=dict(type="dict", options=backup_spec),
        save=dict(type="bool", default=False),
        delete=dict(type="bool", default=False),
        commit=dict(type="bool", default=True),
        validate=dict(type="bool", default=False),
        get_filter=dict(type="raw"),
    )

    mutually_exclusive = [
        ("content", "source_datastore", "delete", "confirm_commit")
    ]
    required_one_of = [
        ("content", "source_datastore", "delete", "confirm_commit")
    ]

    module = AnsibleModule(
        argument_spec=argument_spec,
        required_one_of=required_one_of,
        mutually_exclusive=mutually_exclusive,
        supports_check_mode=True,
    )

    config = module.params["content"]
    target = module.params["target"]
    lock = module.params["lock"]
    source = module.params["source_datastore"]
    delete = module.params["delete"]
    confirm_commit = module.params["confirm_commit"]
    confirm = module.params["confirm"]
    validate = module.params["validate"]
    save = module.params["save"]
    filter = module.params["get_filter"]
    format = module.params["format"]

    try:
        filter_data, filter_type = validate_and_normalize_data(filter)
    except Exception as exc:
        module.fail_json(msg=to_text(exc))

    if filter_type == "xml":
        filter_type = "subtree"
    elif filter_type == "json":
        try:
            filter = dict_to_xml(filter_data)
        except Exception as exc:
            module.fail_json(msg=to_text(exc))
        filter_type = "subtree"
    elif filter_type == "xpath":
        pass
    elif filter_type is None:
        if filter_data is not None:
            # to maintain backward compatibility for ansible 2.9 which
            # defaults to "subtree" filter type
            filter_type = "subtree"
            module.warn(
                "The data format of get_filter option value couldn't be identified, hence set to 'subtree'"
            )
        else:
            pass
    else:
        module.fail_json(
            msg="Invalid filter type detected %s for get_filter value %s"
            % (filter_type, filter)
        )

    conn = Connection(module._socket_path)
    capabilities = get_capabilities(module)
    operations = capabilities["device_operations"]

    supports_commit = operations.get("supports_commit", False)
    supports_writable_running = operations.get(
        "supports_writable_running", False
    )
    supports_startup = operations.get("supports_startup", False)

    # identify target datastore
    if target == "candidate" and not supports_commit:
        module.fail_json(
            msg=":candidate is not supported by this netconf server"
        )
    elif target == "running" and not supports_writable_running:
        module.fail_json(
            msg=":writable-running is not supported by this netconf server"
        )
    elif target == "auto":
        if supports_commit:
            target = "candidate"
        elif supports_writable_running:
            target = "running"
        else:
            module.fail_json(
                msg="neither :candidate nor :writable-running are supported by this netconf server"
            )

    # Netconf server capability validation against input options
    if save and not supports_startup:
        module.fail_json(
            msg="cannot copy <%s/> to <startup/>, while :startup is not supported"
            % target
        )

    if confirm_commit and not operations.get("supports_confirm_commit", False):
        module.fail_json(
            msg="confirm commit is not supported by Netconf server"
        )

    if (confirm > 0) and not operations.get("supports_confirm_commit", False):
        module.fail_json(
            msg="confirm commit is not supported by this netconf server, given confirm timeout: %d"
            % confirm
        )

    if validate and not operations.get("supports_validate", False):
        module.fail_json(
            msg="validate is not supported by this netconf server"
        )

    if filter_type == "xpath" and not operations.get("supports_xpath", False):
        module.fail_json(
            msg="filter value '%s' of type xpath is not supported on this device"
            % filter
        )

    filter_spec = (filter_type, filter) if filter_type else None

    if lock == "never":
        execute_lock = False
    elif target in operations.get("lock_datastore", []):
        # lock is requested (always/if-support) and supported => lets do it
        execute_lock = True
    else:
        # lock is requested (always/if-supported) but not supported => issue warning
        module.warn(
            "lock operation on '%s' source is not supported on this device"
            % target
        )
        execute_lock = lock == "always"

    result = {
        "changed": False,
        "server_capabilities": capabilities.get("server_capabilities", []),
    }
    before = None
    after = None
    locked = False
    try:
        if module.params["backup"]:
            response = get_config(
                module, target, filter_spec, lock=execute_lock
            )
            before = to_text(
                tostring(response), errors="surrogate_then_replace"
            ).strip()
            result["__backup__"] = before.strip()
        if validate:
            conn.validate(target)
        if source:
            if not module.check_mode:
                conn.copy(source, target)
            result["changed"] = True
        elif delete:
            if not module.check_mode:
                conn.delete(target)
            result["changed"] = True
        elif confirm_commit:
            if not module.check_mode:
                conn.commit()
            result["changed"] = True
        elif config:
            if module.check_mode and not supports_commit:
                module.warn(
                    "check mode not supported as Netconf server doesn't support candidate capability"
                )
                result["changed"] = True
                module.exit_json(**result)

            if execute_lock:
                conn.lock(target=target)
                locked = True
            if before is None:
                before = to_text(
                    conn.get_config(source=target, filter=filter_spec),
                    errors="surrogate_then_replace",
                ).strip()

            if format != "text":
                # check for format of type json/xml/xpath
                try:
                    config_obj, config_format = validate_and_normalize_data(
                        config, format
                    )
                except Exception as exc:
                    module.fail_json(msg=to_text(exc))

                if config_format == "json":
                    try:
                        config = dict_to_xml(config_obj)
                    except Exception as exc:
                        module.fail_json(msg=to_text(exc))
                    format = "xml"
                elif config_format is None:
                    format = "xml"
                    module.warn(
                        "The data format of content option value couldn't be identified, hence set to 'xml'"
                    )
                else:
                    format = config_format

            validate_config(module, config, format)

            kwargs = {
                "config": config,
                "target": target,
                "default_operation": module.params["default_operation"],
                "error_option": module.params["error_option"],
                "format": format,
            }

            conn.edit_config(**kwargs)

            if supports_commit and module.params["commit"]:
                after = to_text(
                    conn.get_config(source="candidate", filter=filter_spec),
                    errors="surrogate_then_replace",
                ).strip()
                if not module.check_mode:
                    confirm_timeout = confirm if confirm > 0 else None
                    confirmed_commit = True if confirm_timeout else False
                    conn.commit(
                        confirmed=confirmed_commit, timeout=confirm_timeout
                    )
                else:
                    conn.discard_changes()

            if after is None:
                after = to_text(
                    conn.get_config(source="running", filter=filter_spec),
                    errors="surrogate_then_replace",
                ).strip()

            sanitized_before = sanitize_xml(before)
            sanitized_after = sanitize_xml(after)
            if sanitized_before != sanitized_after:
                result["changed"] = True

            if result["changed"]:
                if save and not module.check_mode:
                    conn.copy_config(target, "startup")
                if module._diff:
                    result["diff"] = {
                        "before": sanitized_before,
                        "after": sanitized_after,
                    }

    except ConnectionError as e:
        module.fail_json(
            msg=to_text(e, errors="surrogate_then_replace").strip()
        )
    finally:
        if locked:
            conn.unlock(target=target)

    module.exit_json(**result)