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)
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)
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)