diff --git a/lib/ansible/module_utils/network/icx/__init__.py b/lib/ansible/module_utils/network/icx/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ansible/module_utils/network/icx/icx.py b/lib/ansible/module_utils/network/icx/icx.py new file mode 100644 index 00000000000..5273927c5da --- /dev/null +++ b/lib/ansible/module_utils/network/icx/icx.py @@ -0,0 +1,102 @@ +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# (c) 2016 Red Hat Inc. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import json +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import env_fallback +from ansible.module_utils.network.common.utils import to_list +from ansible.module_utils.connection import Connection, ConnectionError + +_DEVICE_CONFIGS = {} + + +def get_connection(module): + return Connection(module._socket_path) + + +def load_config(module, commands): + connection = get_connection(module) + + try: + resp = connection.edit_config(candidate=commands) + return resp.get('response') + except ConnectionError as exc: + module.fail_json(msg=to_text(exc)) + + +def run_commands(module, commands, check_rc=True): + connection = get_connection(module) + try: + return connection.run_commands(commands=commands, check_rc=check_rc) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc)) + + +def exec_scp(module, command): + connection = Connection(module._socket_path) + return connection.scp(**command) + + +def get_config(module, flags=None, compare=None): + flag_str = ' '.join(to_list(flags)) + try: + return _DEVICE_CONFIGS[flag_str] + except KeyError: + connection = get_connection(module) + try: + out = connection.get_config(flags=flags, compare=compare) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) + cfg = to_text(out, errors='surrogate_then_replace').strip() + _DEVICE_CONFIGS[flag_str] = cfg + return cfg + + +def check_args(module, warnings): + pass + + +def get_defaults_flag(module): + connection = get_connection(module) + try: + out = connection.get_defaults_flag() + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) + return to_text(out, errors='surrogate_then_replace').strip() + + +def get_env_diff(module, compare=None): + connection = get_connection(module) + try: + out = connection.get_env_diff(compare) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) + return out diff --git a/lib/ansible/modules/network/icx/__init__.py b/lib/ansible/modules/network/icx/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ansible/modules/network/icx/icx_banner.py b/lib/ansible/modules/network/icx/icx_banner.py new file mode 100644 index 00000000000..8e8106d4d01 --- /dev/null +++ b/lib/ansible/modules/network/icx/icx_banner.py @@ -0,0 +1,217 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# 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 + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'network'} + + +DOCUMENTATION = """ +--- +module: icx_banner +version_added: "2.9" +author: "Ruckus Wireless (@Commscope)" +short_description: Manage multiline banners on Ruckus ICX 7000 series switches +description: + - This will configure both login and motd banners on remote + ruckus ICX 7000 series switches. It allows playbooks to add or remove + banner text from the active running configuration. +options: + banner: + description: + - Specifies which banner should be configured on the remote device. + type: str + required: true + choices: ['motd', 'exec', 'incoming'] + text: + description: + - The banner text that should be + present in the remote device running configuration. + This argument accepts a multiline string, with no empty lines. + type: str + state: + description: + - Specifies whether or not the configuration is + present in the current devices active running configuration. + type: str + default: present + choices: ['present', 'absent'] + enterkey: + description: + - Specifies whether or not the motd configuration should accept + the require-enter-key + type: bool + default: no + check_running_config: + description: + - Check running configuration. This can be set as environment variable. + Module will use environment variable value(default:True), unless it is overriden, + by specifying it as module parameter. + type: bool + default: yes +""" + +EXAMPLES = """ +- name: configure the motd banner + icx_banner: + banner: motd + text: | + this is my motd banner + that contains a multiline + string + state: present + +- name: remove the motd banner + icx_banner: + banner: motd + state: absent + +- name: configure require-enter-key for motd + icx_banner: + banner: motd + enterkey: True + +- name: remove require-enter-key for motd + icx_banner: + banner: motd + enterkey: False +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - banner motd + - this is my motd banner + - that contains a multiline + - string +""" + +import re +from ansible.module_utils._text import to_text +from ansible.module_utils.connection import exec_command +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.network.icx.icx import load_config, get_config, get_env_diff +from ansible.module_utils.connection import Connection, ConnectionError + + +def map_obj_to_commands(updates, module): + commands = list() + state = module.params['state'] + want, have = updates + + if module.params['banner'] != 'motd' and module.params['enterkey']: + module.fail_json(msg=module.params['banner'] + " banner can have text only, got enterkey") + + if state == 'absent': + if 'text' in have.keys() and have['text']: + commands.append('no banner %s' % module.params['banner']) + if(module.params['enterkey'] is False): + commands.append('no banner %s require-enter-key' % module.params['banner']) + + elif state == 'present': + if module.params['text'] is None and module.params['enterkey'] is None: + module.fail_json(msg=module.params['banner'] + " one of the following is required: text, enterkey:only if motd") + + if module.params["banner"] == "motd" and want['enterkey'] != have['enterkey']: + if(module.params['enterkey']): + commands.append('banner %s require-enter-key' % module.params['banner']) + + if want['text'] and (want['text'] != have.get('text')): + module.params["enterkey"] = None + banner_cmd = 'banner %s' % module.params['banner'] + banner_cmd += ' $\n' + banner_cmd += module.params['text'].strip() + banner_cmd += '\n$' + commands.append(banner_cmd) + return commands + + +def map_config_to_obj(module): + compare = module.params.get('check_running_config') + obj = {'banner': module.params['banner'], 'state': 'absent', 'enterkey': False} + exec_command(module, 'skip') + output_text = '' + output_re = '' + out = get_config(module, flags=['| begin banner %s' + % module.params['banner']], compare=module.params['check_running_config']) + if out: + try: + output_re = re.search(r'banner %s( require-enter-key)' % module.params['banner'], out, re.S).group(0) + obj['enterkey'] = True + except BaseException: + pass + try: + output_text = re.search(r'banner %s (\$([^\$])+\$){1}' % module.params['banner'], out, re.S).group(1).strip('$\n') + except BaseException: + pass + + else: + output_text = None + if output_text: + obj['text'] = output_text + obj['state'] = 'present' + if get_env_diff(module, module.params['check_running_config']) is False: + obj = {'banner': module.params['banner'], 'state': 'absent', 'enterkey': False, 'text': 'JUNK'} + return obj + + +def map_params_to_obj(module): + text = module.params['text'] + if text: + text = str(text).strip() + + return { + 'banner': module.params['banner'], + 'text': text, + 'state': module.params['state'], + 'enterkey': module.params['enterkey'] + } + + +def main(): + """entry point for module execution + """ + argument_spec = dict( + banner=dict(required=True, choices=['motd', 'exec', 'incoming']), + text=dict(), + enterkey=dict(type='bool'), + state=dict(default='present', choices=['present', 'absent']), + check_running_config=dict(default=True, type='bool') + ) + + required_one_of = [['text', 'enterkey', 'state']] + module = AnsibleModule(argument_spec=argument_spec, + required_one_of=required_one_of, + supports_check_mode=True) + + warnings = list() + results = {'changed': False} + + want = map_params_to_obj(module) + have = map_config_to_obj(module) + + # results['want']=want + # results['have']=have + + commands = map_obj_to_commands((want, have), module) + results['commands'] = commands + + if commands: + if not module.check_mode: + response = load_config(module, commands) + + results['changed'] = True + + module.exit_json(**results) + + +if __name__ == '__main__': + main()