File: //lib/python3.9/site-packages/ansible_collections/cisco/iosxr/plugins/modules/iosxr_banner.py
# -*- coding: utf-8 -*-
# Copyright: (c) 2017, Ansible by Red Hat, inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: iosxr_banner
author:
- Trishna Guha (@trishnaguha)
- Kedar Kekan (@kedarX)
short_description: Module to configure multiline banners.
description:
- This module will configure both exec and motd banners on remote device running Cisco
IOS XR. It allows playbooks to add or remove banner text from the running configuration.
version_added: 1.0.0
requirements:
- ncclient >= 0.5.3 when using netconf
- lxml >= 4.1.1 when using netconf
extends_documentation_fragment:
- cisco.iosxr.iosxr
notes:
- This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR
Platform Options,../network/user_guide/platform_iosxr.html).
options:
banner:
description:
- Specifies the type of banner to configure on remote device.
required: true
type: str
choices:
- login
- motd
text:
description:
- Banner text to be configured. Accepts multi line string, without empty lines.
When using a multi line string, the first and last characters must be the
start and end delimiters for the banner
Requires I(state=present).
type: str
state:
description:
- Existential state of the configuration on the device.
default: present
type: str
choices:
- present
- absent
"""
EXAMPLES = """
- name: configure the login banner
cisco.iosxr.iosxr_banner:
banner: login
text: |
@this is my login banner
that contains a multiline
string@
state: present
- name: remove the motd banner
cisco.iosxr.iosxr_banner:
banner: motd
state: absent
- name: Configure banner from file
cisco.iosxr.iosxr_banner:
banner: motd
text: "{{ lookup('file', './config_partial/raw_banner.cfg') }}"
state: present
"""
RETURN = """
commands:
description: The list of configuration mode commands sent to device with transport C(cli)
returned: always (empty list when no commands to send)
type: list
sample:
- banner login
- "@this is my login banner"
- that contains a multiline
- string@
xml:
description: NetConf rpc xml sent to device with transport C(netconf)
returned: always (empty list when no xml rpc to send)
type: list
sample:
- '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<banners xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-infra-infra-cfg">
<banner xc:operation="merge">
<banner-name>motd</banner-name>
<banner-text>Ansible banner example</banner-text>
</banner>
</banners>
</config>'
"""
import collections
import re
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import (
build_xml,
etree_find,
get_config,
is_cliconf,
is_netconf,
load_config,
)
class ConfigBase(object):
def __init__(self, module):
self._module = module
self._result = {"changed": False, "warnings": []}
self._want = {}
self._have = {}
def map_params_to_obj(self):
text = self._module.params["text"]
if text:
text = "{0}".format(str(text).strip())
self._want.update(
{
"banner": self._module.params["banner"],
"text": text,
"state": self._module.params["state"],
},
)
class CliConfiguration(ConfigBase):
def __init__(self, module):
super(CliConfiguration, self).__init__(module)
def map_obj_to_commands(self):
commands = list()
state = self._module.params["state"]
if state == "absent":
if self._have.get("state") != "absent" and (
"text" in self._have.keys() and self._have["text"]
):
commands.append("no banner {0!s}".format(self._module.params["banner"]))
elif state == "present":
if self._want["text"] and self._want["text"].encode().decode(
"unicode_escape",
) != self._have.get("text"):
banner_cmd = "banner {0!s} ".format(self._module.params["banner"])
banner_cmd += self._want["text"].strip()
commands.append(banner_cmd)
self._result["commands"] = commands
if commands:
commit = not self._module.check_mode
diff = load_config(self._module, commands, commit=commit)
if diff:
self._result["diff"] = dict(prepared=diff)
self._result["changed"] = True
def map_config_to_obj(self):
cli_filter = "banner {0!s}".format(self._module.params["banner"])
output = get_config(self._module, config_filter=cli_filter)
match = re.search(r"banner (\S+) (.*)", output, re.DOTALL)
if match:
text = match.group(2)
else:
text = None
obj = {"banner": self._module.params["banner"], "state": "absent"}
if output:
obj["text"] = text
obj["state"] = "present"
self._have.update(obj)
def run(self):
self.map_params_to_obj()
self.map_config_to_obj()
self.map_obj_to_commands()
return self._result
class NCConfiguration(ConfigBase):
def __init__(self, module):
super(NCConfiguration, self).__init__(module)
self._banners_meta = collections.OrderedDict()
self._banners_meta.update(
[
("banner", {"xpath": "banners/banner", "tag": True, "attrib": "operation"}),
("a:banner", {"xpath": "banner/banner-name"}),
("a:text", {"xpath": "banner/banner-text", "operation": "edit"}),
],
)
def map_obj_to_xml_rpc(self):
state = self._module.params["state"]
_get_filter = build_xml(
"banners",
xmap=self._banners_meta,
params=self._module.params,
opcode="filter",
)
running = get_config(self._module, source="running", config_filter=_get_filter)
banner_name = None
banner_text = None
if etree_find(running, "banner-text") is not None:
banner_name = etree_find(running, "banner-name").text
banner_text = etree_find(running, "banner-text").text
opcode = None
if state == "absent" and banner_name == self._module.params["banner"] and len(banner_text):
opcode = "delete"
elif state == "present":
opcode = "merge"
self._result["xml"] = []
if opcode:
_edit_filter = build_xml(
"banners",
xmap=self._banners_meta,
params=self._module.params,
opcode=opcode,
)
if _edit_filter is not None:
commit = not self._module.check_mode
diff = load_config(
self._module,
_edit_filter,
commit=commit,
running=running,
nc_get_filter=_get_filter,
)
if diff:
self._result["xml"] = _edit_filter
if self._module._diff:
self._result["diff"] = dict(prepared=diff)
self._result["changed"] = True
def run(self):
self.map_params_to_obj()
self.map_obj_to_xml_rpc()
return self._result
def main():
"""main entry point for module execution"""
argument_spec = dict(
banner=dict(required=True, choices=["login", "motd"]),
text=dict(),
state=dict(default="present", choices=["present", "absent"]),
)
required_if = [("state", "present", ("text",))]
module = AnsibleModule(
argument_spec=argument_spec,
required_if=required_if,
supports_check_mode=True,
)
config_object = None
if is_cliconf(module):
# Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported
config_object = CliConfiguration(module)
elif is_netconf(module):
config_object = NCConfiguration(module)
result = None
if config_object is not None:
result = config_object.run()
module.exit_json(**result)
if __name__ == "__main__":
main()