diff options
Diffstat (limited to 'lib/ansible/modules/network/icx')
-rw-r--r-- | lib/ansible/modules/network/icx/icx_banner.py | 216 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_command.py | 233 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_config.py | 484 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_copy.py | 373 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_facts.py | 549 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_interface.py | 694 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_l3_interface.py | 439 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_linkagg.py | 328 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_lldp.py | 184 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_logging.py | 582 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_ping.py | 270 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_static_route.py | 315 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_system.py | 471 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_user.py | 391 | ||||
-rw-r--r-- | lib/ansible/modules/network/icx/icx_vlan.py | 784 |
15 files changed, 0 insertions, 6313 deletions
diff --git a/lib/ansible/modules/network/icx/icx_banner.py b/lib/ansible/modules/network/icx/icx_banner.py deleted file mode 100644 index 086d6fdd37..0000000000 --- a/lib/ansible/modules/network/icx/icx_banner.py +++ /dev/null @@ -1,216 +0,0 @@ -#!/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': 'community'} - - -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. -notes: - - Tested against ICX 10.1 -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 overridden, - 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, env_fallback -from ansible.module_utils.network.icx.icx import load_config, get_config -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 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', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - - 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) - 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() diff --git a/lib/ansible/modules/network/icx/icx_command.py b/lib/ansible/modules/network/icx/icx_command.py deleted file mode 100644 index 092745c741..0000000000 --- a/lib/ansible/modules/network/icx/icx_command.py +++ /dev/null @@ -1,233 +0,0 @@ -#!/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': 'community'} - - -DOCUMENTATION = """ ---- -module: icx_command -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Run arbitrary commands on remote Ruckus ICX 7000 series switches -description: - - Sends arbitrary commands to an ICX node and returns the results - read from the device. This module includes an - argument that will cause the module to wait for a specific condition - before returning or timing out if the condition is not met. -notes: - - Tested against ICX 10.1 -options: - commands: - description: - - List of commands to send to the remote ICX device over the - configured provider. The resulting output from the command - is returned. If the I(wait_for) argument is provided, the - module is not returned until the condition is satisfied or - the number of retries has expired. If a command sent to the - device requires answering a prompt, checkall and newline if - multiple prompts, it is possible to pass - a dict containing I(command), I(answer), I(prompt), I(check_all) - and I(newline).Common answers are 'y' or "\\r" (carriage return, - must be double quotes). See examples. - type: list - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - type: list - aliases: ['waitfor'] - match: - description: - - The I(match) argument is used in conjunction with the - I(wait_for) argument to specify the match policy. Valid - values are C(all) or C(any). If the value is set to C(all) - then all conditionals in the wait_for must be satisfied. If - the value is set to C(any) then only one of the values must be - satisfied. - type: str - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of times a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - type: int - default: 10 - interval: - description: - - Configures the interval in seconds to wait between retries - of the command. If the command does not pass the specified - conditions, the interval indicates how long to wait before - trying the command again. - type: int - default: 1 -""" - -EXAMPLES = """ -tasks: - - name: run show version on remote devices - icx_command: - commands: show version - - - name: run show version and check to see if output contains ICX - icx_command: - commands: show version - wait_for: result[0] contains ICX - - - name: run multiple commands on remote nodes - icx_command: - commands: - - show version - - show interfaces - - - name: run multiple commands and evaluate the output - icx_command: - commands: - - show version - - show interfaces - wait_for: - - result[0] contains ICX - - result[1] contains GigabitEthernet1/1/1 - - name: run commands that require answering a prompt - icx_command: - commands: - - command: 'service password-encryption sha1' - prompt: 'Warning: Moving to higher password-encryption type,.*' - answer: 'y' - - name: run commands that require answering multiple prompt - icx_command: - commands: - - command: 'username qqq password qqq' - prompt: - - 'User already exists. Do you want to modify:.*' - - 'To modify or remove user, enter current password:' - answer: - - 'y' - - 'qqq\\\r' - check_all: True - newline: False -""" - -RETURN = """ -stdout: - description: The set of responses from the commands - returned: always apart from low level errors - type: list - sample: ['...', '...'] -stdout_lines: - description: The value of stdout split into a list - returned: always apart from low level errors - type: list - sample: [['...', '...'], ['...'], ['...']] -failed_conditions: - description: The list of conditionals that have failed - returned: failed - type: list - sample: ['...', '...'] -""" - - -import re -import time -from ansible.module_utils.network.icx.icx import run_commands -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.common.utils import ComplexList, to_lines -from ansible.module_utils.network.common.parsing import Conditional -from ansible.module_utils.six import string_types - - -def parse_commands(module, warnings): - command = ComplexList(dict( - command=dict(key=True), - prompt=dict(), - answer=dict(), - check_all=dict(type='bool', default='False'), - newline=dict(type='bool', default='True') - ), module) - commands = command(module.params['commands']) - for item in list(commands): - if module.check_mode: - if not item['command'].startswith('show'): - warnings.append( - 'Only show commands are supported when using check mode, not executing configure terminal') - commands.remove(item) - return commands - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list', aliases=['waitfor']), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - run_commands(module, ['skip']) - commands = parse_commands(module, warnings) - result['warnings'] = warnings - - wait_for = module.params['wait_for'] or list() - conditionals = [Conditional(c) for c in wait_for] - retries = module.params['retries'] - interval = module.params['interval'] - match = module.params['match'] - - while retries > 0: - - responses = run_commands(module, commands) - - for item in list(conditionals): - - if item(responses): - if match == 'any': - conditionals = list() - break - conditionals.remove(item) - - if not conditionals: - break - - time.sleep(interval) - retries -= 1 - - if conditionals: - failed_conditions = [item.raw for item in conditionals] - msg = 'One or more conditional statements have not been satisfied' - module.fail_json(msg=msg, failed_conditions=failed_conditions) - - result.update({ - 'changed': False, - 'stdout': responses, - 'stdout_lines': list(to_lines(responses)) - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/network/icx/icx_config.py b/lib/ansible/modules/network/icx/icx_config.py deleted file mode 100644 index 44f43b6f47..0000000000 --- a/lib/ansible/modules/network/icx/icx_config.py +++ /dev/null @@ -1,484 +0,0 @@ -#!/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': 'community'} - -DOCUMENTATION = """ ---- -module: icx_config -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Manage configuration sections of Ruckus ICX 7000 series switches -description: - - Ruckus ICX configurations use a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with ICX configuration sections in - a deterministic way. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - lines: - description: - - The ordered set of commands that should be configured in the - section. The commands must be the exact same commands as found - in the device running-config. Be sure to note the configuration - command syntax as some commands are automatically modified by the - device config parser. - type: list - aliases: ['commands'] - parents: - description: - - The ordered set of parents that uniquely identify the section or hierarchy - the commands should be checked against. If the parents argument - is omitted, the commands are checked against the set of top - level or global commands. - type: list - src: - description: - - Specifies the source path to the file that contains the configuration - or configuration template to load. The path to the source file can - either be the full path on the Ansible control host or a relative - path from the playbook or role root directory. This argument is mutually - exclusive with I(lines), I(parents). - type: str - before: - description: - - The ordered set of commands to push on to the command stack if - a change needs to be made. This allows the playbook designer - the opportunity to perform configuration commands prior to pushing - any changes without affecting how the set of commands are matched - against the system. - type: list - after: - description: - - The ordered set of commands to append to the end of the command - stack if a change needs to be made. Just like with I(before) this - allows the playbook designer to append a set of commands to be - executed after the command set. - type: list - match: - description: - - Instructs the module on the way to perform the matching of - the set of commands against the current device config. If - match is set to I(line), commands are matched line by line. If - match is set to I(strict), command lines are matched with respect - to position. If match is set to I(exact), command lines - must be an equal match. Finally, if match is set to I(none), the - module will not attempt to compare the source configuration with - the running configuration on the remote device. - type: str - choices: ['line', 'strict', 'exact', 'none'] - default: line - replace: - description: - - Instructs the module on the way to perform the configuration - on the device. If the replace argument is set to I(line) then - the modified lines are pushed to the device in configuration - mode. If the replace argument is set to I(block) then the entire - command block is pushed to the device in configuration mode if any - line is not correct. - type: str - default: line - choices: ['line', 'block'] - multiline_delimiter: - description: - - This argument is used when pushing a multiline configuration - element to the ICX device. It specifies the character to use - as the delimiting character. This only applies to the - configuration action. - type: str - default: "@" - backup: - description: - - This argument will cause the module to create a full backup of - the current C(running-config) from the remote device before any - changes are made. The backup file is written to the C(backup) - folder in the playbook root directory or role root directory, if - playbook is part of an ansible role. If the directory does not exist, - it is created. - type: bool - default: 'no' - defaults: - description: - - This argument specifies whether or not to collect all defaults - when getting the remote device running config. When enabled, - the module will get the current config by issuing the command - C(show running-config all). - type: bool - default: 'no' - running_config: - description: - - The module, by default, will connect to the remote device and - retrieve the current running-config to use as a base for comparing - against the contents of source. There are times when it is not - desirable to have the task get the current running-config for - every task in a playbook. The I(running_config) argument allows the - implementer to pass in the configuration to use as the base - config for comparison. - type: str - aliases: ['config'] - save_when: - description: - - When changes are made to the device running-configuration, the - changes are not copied to non-volatile storage by default. Using - this argument will change that before. If the argument is set to - I(always), then the running-config will always be copied to the - start-up configuration and the I(modified) flag will always be set to - True. If the argument is set to I(modified), then the running-config - will only be copied to the start-up configuration if it has changed since - the last save to configuration. If the argument is set to - I(never), the running-config will never be copied to the - configuration. If the argument is set to I(changed), then the running-config - will only be copied to the configuration if the task has made a change. - type: str - default: never - choices: ['always', 'never', 'modified', 'changed'] - diff_against: - description: - - When using the C(ansible-playbook --diff) command line argument - the module can generate diffs against different sources. - - When this option is configure as I(startup), the module will return - the diff of the running-config against the configuration. - - When this option is configured as I(intended), the module will - return the diff of the running-config against the configuration - provided in the C(intended_config) argument. - - When this option is configured as I(running), the module will - return the before and after diff of the running-config with respect - to any changes made to the device configuration. - type: str - choices: ['running', 'startup', 'intended'] - diff_ignore_lines: - description: - - Use this argument to specify one or more lines that should be - ignored during the diff. This is used for lines in the configuration - that are automatically updated by the system. This argument takes - a list of regular expressions or exact line matches. - type: list - intended_config: - description: - - The C(intended_config) provides the master configuration that - the node should conform to and is used to check the final - running-config against. This argument will not modify any settings - on the remote device and is strictly used to check the compliance - of the current device's configuration against. When specifying this - argument, the task should also modify the C(diff_against) value and - set it to I(intended). - type: str -""" - -EXAMPLES = """ -- name: configure top level configuration - icx_config: - lines: hostname {{ inventory_hostname }} - -- name: configure interface settings - icx_config: - lines: - - port-name test string - - ip address 172.31.1.1 255.255.255.0 - parents: interface ethernet 1/1/2 - -- name: configure ip helpers on multiple interfaces - icx_config: - lines: - - ip helper-address 172.26.1.10 - - ip helper-address 172.26.3.8 - parents: "{{ item }}" - with_items: - - interface ethernet 1/1/2 - - interface ethernet 1/1/3 - -- name: load new acl into device - icx_config: - lines: - - permit ip host 192.0.2.1 any log - - permit ip host 192.0.2.2 any log - - permit ip host 192.0.2.3 any log - - permit ip host 192.0.2.4 any log - parents: ip access-list extended test - before: no ip access-list extended test - match: exact - -- name: check the running-config against master config - icx_config: - diff_against: intended - intended_config: "{{ lookup('file', 'master.cfg') }}" - -- name: check the configuration against the running-config - icx_config: - diff_against: startup - diff_ignore_lines: - - ntp clock .* - -- name: for idempotency, use full-form commands - icx_config: - lines: - # - en - - enable - # parents: int eth1/0/11 - parents: interface ethernet 1/1/2 - -# Set boot image based on comparison to a group_var (version) and the version -# that is returned from the `icx_facts` module -- name: SETTING BOOT IMAGE - icx_config: - lines: - - no boot system - - boot system flash bootflash:{{new_image}} - host: "{{ inventory_hostname }}" - when: ansible_net_version != version - -- name: render template onto an ICX device - icx_config: - backup: yes - src: "{{ lookup('file', 'config.j2') }}" -""" - -RETURN = """ -updates: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['hostname foo', 'router ospf 1', 'router-id 192.0.2.1'] -commands: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['hostname foo', 'router ospf 1', 'router-id 192.0.2.1'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/icx_config.2016-07-16@22:28:34 -""" - -import json -from ansible.module_utils._text import to_text -from ansible.module_utils.connection import ConnectionError -from ansible.module_utils.network.icx.icx import run_commands, get_config -from ansible.module_utils.network.icx.icx import get_defaults_flag, get_connection -from ansible.module_utils.network.icx.icx import check_args as icx_check_args -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.common.config import NetworkConfig, dumps - - -def check_args(module, warnings): - icx_check_args(module, warnings) - if module.params['multiline_delimiter']: - if len(module.params['multiline_delimiter']) != 1: - module.fail_json(msg='multiline_delimiter value can only be a ' - 'single character') - - -def edit_config_or_macro(connection, commands): - if "macro name" in commands[0]: - connection.edit_macro(candidate=commands) - else: - if commands[0] != '': - connection.edit_config(candidate=commands) - - -def get_candidate_config(module): - candidate = '' - if module.params['src']: - candidate = module.params['src'] - - elif module.params['lines']: - candidate_obj = NetworkConfig(indent=1) - parents = module.params['parents'] or list() - candidate_obj.add(module.params['lines'], parents=parents) - candidate = dumps(candidate_obj, 'raw') - - return candidate - - -def get_running_config(module, current_config=None, flags=None): - running = module.params['running_config'] - if not running: - if not module.params['defaults'] and current_config: - running = current_config - else: - running = get_config(module, flags=flags) - - return running - - -def save_config(module, result): - result['changed'] = True - if not module.check_mode: - run_commands(module, 'write memory') - else: - module.warn('Skipping command `copy running-config start-up configuration` ' - 'due to check_mode. Configuration not copied to ' - 'non-volatile storage') - - -def main(): - """ main entry point for module execution - """ - argument_spec = dict( - src=dict(), - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block']), - multiline_delimiter=dict(default='@'), - - running_config=dict(aliases=['config']), - intended_config=dict(), - - defaults=dict(type='bool', default=False), - backup=dict(type='bool', default=False), - - save_when=dict(choices=['always', 'never', 'modified', 'changed'], default='never'), - - diff_against=dict(choices=['startup', 'intended', 'running']), - diff_ignore_lines=dict(type='list'), - - ) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines']), - ('diff_against', 'intended', ['intended_config'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - check_args(module, warnings) - result['warnings'] = warnings - run_commands(module, 'skip') - diff_ignore_lines = module.params['diff_ignore_lines'] - config = None - contents = None - flags = None if module.params['defaults'] else [] - connection = get_connection(module) - - if module.params['backup'] or (module._diff and module.params['diff_against'] == 'running'): - contents = get_config(module, flags=flags) - config = NetworkConfig(indent=1, contents=contents) - if module.params['backup']: - result['__backup__'] = contents - - if any((module.params['lines'], module.params['src'])): - match = module.params['match'] - replace = module.params['replace'] - path = module.params['parents'] - - candidate = get_candidate_config(module) - running = get_running_config(module, contents, flags=flags) - try: - response = connection.get_diff(candidate=candidate, running=running, diff_match=match, diff_ignore_lines=diff_ignore_lines, path=path, - diff_replace=replace) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - config_diff = response['config_diff'] - banner_diff = response['banner_diff'] - - if config_diff or banner_diff: - commands = config_diff.split('\n') - - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - result['commands'] = commands - result['updates'] = commands - result['banners'] = banner_diff - - # send the configuration commands to the device and merge - # them with the current running config - if not module.check_mode: - if commands: - edit_config_or_macro(connection, commands) - if banner_diff: - connection.edit_banner(candidate=json.dumps(banner_diff), multiline_delimiter=module.params['multiline_delimiter']) - - result['changed'] = True - - running_config = module.params['running_config'] - startup_config = None - - if module.params['save_when'] == 'always': - save_config(module, result) - elif module.params['save_when'] == 'modified': - output = run_commands(module, ['show running-config', 'show configuration']) - - running_config = NetworkConfig(indent=1, contents=output[0], ignore_lines=diff_ignore_lines) - startup_config = NetworkConfig(indent=1, contents=output[1], ignore_lines=diff_ignore_lines) - - if running_config.sha1 != startup_config.sha1: - save_config(module, result) - elif module.params['save_when'] == 'changed' and result['changed']: - save_config(module, result) - - if module._diff: - if not running_config: - output = run_commands(module, 'show running-config') - contents = output[0] - else: - contents = running_config - - # recreate the object in order to process diff_ignore_lines - running_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) - - if module.params['diff_against'] == 'running': - if module.check_mode: - module.warn("unable to perform diff against running-config due to check mode") - contents = None - else: - contents = config.config_text - - elif module.params['diff_against'] == 'startup': - if not startup_config: - output = run_commands(module, 'show configuration') - contents = output[0] - else: - contents = startup_config.config_text - - elif module.params['diff_against'] == 'intended': - contents = module.params['intended_config'] - - if contents is not None: - base_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) - - if running_config.sha1 != base_config.sha1: - if module.params['diff_against'] == 'intended': - before = running_config - after = base_config - elif module.params['diff_against'] in ('startup', 'running'): - before = base_config - after = running_config - - result.update({ - 'changed': True, - 'diff': {'before': str(before), 'after': str(after)} - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/network/icx/icx_copy.py b/lib/ansible/modules/network/icx/icx_copy.py deleted file mode 100644 index 08f2e8366d..0000000000 --- a/lib/ansible/modules/network/icx/icx_copy.py +++ /dev/null @@ -1,373 +0,0 @@ -#!/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': 'community'} - - -DOCUMENTATION = """ ---- -module: icx_copy -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Transfer files from or to remote Ruckus ICX 7000 series switches -description: - - This module transfers files from or to remote devices running ICX. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - upload: - description: - - Name of the resource to be uploaded. Mutually exclusive with download. - type: str - choices: ['running-config', 'startup-config', 'flash_primary', 'flash_secondary'] - download: - description: - - Name of the resource to be downloaded. Mutually exclusive with upload. - type: str - choices: ['running-config', 'startup-config', 'flash_primary', 'flash_secondary', 'bootrom', 'fips-primary-sig', 'fips-secondary-sig', 'fips-bootrom-sig'] - protocol: - description: - - Data transfer protocol to be used - type: str - choices: ['scp', 'https'] - required: true - remote_server: - description: - - IP address of the remote server - type: str - required: true - remote_port: - description: - - The port number of the remote host. Default values will be selected based on protocol type. - Default scp:22, http:443 - type: str - remote_filename: - description: - - The name or path of the remote file/resource to be uploaded or downloaded. - type: str - required: true - remote_user: - description: - - remote username to be used for scp login. - type: str - remote_pass: - description: - - remote password to be used for scp login. - type: str - public_key: - description: - - public key type to be used to login to scp server - type: str - choices: ['rsa', 'dsa'] - -""" - -EXAMPLES = """ -- name: upload running-config to the remote scp server - icx_copy: - upload: running-config - protocol: scp - remote_server: 172.16.10.49 - remote_filename: running.conf - remote_user: user1 - remote_pass: pass123 - -- name: download running-config from the remote scp server - icx_copy: - download: running-config - protocol: scp - remote_server: 172.16.10.49 - remote_filename: running.conf - remote_user: user1 - remote_pass: pass123 - -- name: download running-config from the remote scp server using rsa public key - icx_copy: - download: running-config - protocol: scp - remote_server: 172.16.10.49 - remote_filename: running.conf - remote_user: user1 - remote_pass: pass123 - public_key: rsa - -- name: upload startup-config to the remote https server - icx_copy: - upload: startup-config - protocol: https - remote_server: 172.16.10.49 - remote_filename: config/running.conf - remote_user: user1 - remote_pass: pass123 - -- name: upload startup-config to the remote https server - icx_copy: - upload: startup-config - protocol: https - remote_server: 172.16.10.49 - remote_filename: config/running.conf - remote_user: user1 - remote_pass: pass123 - -- name: Download OS image into the flash from remote scp ipv6 server - icx_copy: - download: startup-config - protocol: scp - remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C - remote_filename: img.bin - remote_user: user1 - remote_pass: pass123 - -- name: Download OS image into the secondary flash from remote scp ipv6 server - icx_copy: - Download: flash_secondary - protocol: scp - remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C - remote_filename: img.bin - remote_user: user1 - remote_pass: pass123 - -- name: Download OS image into the secondary flash from remote scp ipv6 server on port 5000 - icx_copy: - Download: flash_secondary - protocol: scp - remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C - remote_port: 5000 - remote_filename: img.bin - remote_user: user1 - remote_pass: pass123 - -- name: Download OS image into the primary flash from remote https ipv6 server - icx_copy: - Download: flash_primary - protocol: https - remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C - remote_filename: images/img.bin - remote_user: user1 - remote_pass: pass123 - -- name: Download OS image into the primary flash from remote https ipv6 server on port 8080 - icx_copy: - Download: flash_primary - protocol: https - remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C - remote_port: 8080 - remote_filename: images/img.bin - remote_user: user1 - remote_pass: pass123 -""" - -RETURN = """ -changed: - description: true when downloaded any configuration or flash. false otherwise. - returned: always - type: bool -""" - - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import ConnectionError, exec_command -from ansible.module_utils.network.icx.icx import exec_scp, run_commands - - -def map_params_to_obj(module): - command = dict() - - if(module.params['protocol'] == 'scp'): - if(module.params['upload'] is not None): - module.params["upload"] = module.params["upload"].replace("flash_primary", "primary") - module.params["upload"] = module.params["upload"].replace("flash_secondary", "secondary") - if(module.params["upload"] == 'running-config' or module.params["upload"] == 'startup-config'): - command["command"] = "copy %s scp %s%s %s%s" % (module.params['upload'], - module.params["remote_server"], - " " + module.params["remote_port"] if module.params["remote_port"] else "", - module.params["remote_filename"], - "public-key " + module.params["public_key"] if module.params["public_key"] else "") - else: - command["command"] = "copy flash scp %s%s %s%s %s" % (module.params["remote_server"], - " " + module.params["remote_port"] if module.params["remote_port"] else "", - module.params["remote_filename"], - "public-key " + module.params["public_key"] if module.params["public_key"] else "", - module.params["upload"]) - command["scp_user"] = module.params["remote_user"] - command["scp_pass"] = module.params["remote_pass"] - if(module.params['download'] is not None): - module.params["download"] = module.params["download"].replace("flash_primary", "primary") - module.params["download"] = module.params["download"].replace("flash_secondary", "secondary") - if(module.params["download"] == 'running-config' or module.params["download"] == 'startup-config'): - command["command"] = "copy scp %s %s%s %s%s" % (module.params['download'], - module.params["remote_server"], - " " + module.params["remote_port"] if module.params["remote_port"] else "", - module.params["remote_filename"], - "public-key " + module.params["public_key"] if module.params["public_key"] else "") - else: - command["command"] = "copy scp flash %s%s %s%s %s" % (module.params["remote_server"], - " " + module.params["remote_port"] if module.params["remote_port"] else "", - module.params["remote_filename"], - "public-key " + module.params["public_key"] if module.params["public_key"] else "", - module.params["download"]) - command["scp_user"] = module.params["remote_user"] - command["scp_pass"] = module.params["remote_pass"] - if(module.params['protocol'] == 'https'): - if(module.params['upload'] is not None): - module.params["upload"] = module.params["upload"].replace("flash_primary", "primary") - module.params["upload"] = module.params["upload"].replace("flash_secondary", "secondary") - if(module.params["upload"] == 'running-config' or module.params["upload"] == 'startup-config'): - command["command"] = "copy %s https %s %s%s" % (module.params['upload'], - module.params["remote_server"], - module.params["remote_filename"], - " port " + module.params["remote_port"] if module.params["remote_port"] else "") - else: - command["command"] = "copy https flash %s %s %s%s" % (module.params["remote_server"], - module.params["remote_filename"], - module.params['upload'], - " port " + module.params["remote_port"] if module.params["remote_port"] else "") - if(module.params['download'] is not None): - module.params["download"] = module.params["download"].replace("flash_primary", "primary") - module.params["download"] = module.params["download"].replace("flash_secondary", "secondary") - if(module.params["download"] == 'running-config' or module.params["download"] == 'startup-config'): - command["command"] = "copy https %s %s %s%s" % (module.params['download'], - module.params["remote_server"], - module.params["remote_filename"], - " port " + module.params["remote_port"] if module.params["remote_port"] else "") - else: - command["command"] = "copy https flash %s %s %s%s" % (module.params["remote_server"], - module.params["remote_filename"], - module.params['download'], - " port " + module.params["remote_port"] if module.params["remote_port"] else "") - return command - - -def checkValidations(module): - validation = dict( - scp=dict( - upload=[ - 'running-config', - 'startup-config', - 'flash_primary', - 'flash_secondary'], - download=[ - 'running-config', - 'startup-config', - 'flash_primary', - 'flash_secondary', - 'bootrom', - 'fips-primary-sig', - 'fips-secondary-sig', - 'fips-bootrom-sig']), - https=dict( - upload=[ - 'running-config', - 'startup-config'], - download=[ - 'flash_primary', - 'flash_secondary', - 'startup-config'])) - protocol = module.params['protocol'] - upload = module.params['upload'] - download = module.params['download'] - - if(protocol == 'scp' and module.params['remote_user'] is None): - module.fail_json(msg="While using scp remote_user argument is required") - if(upload is None and download is None): - module.fail_json(msg="Upload or download params are required.") - if(upload is not None and download is not None): - module.fail_json(msg="Only upload or download can be used at a time.") - if(upload): - if(not (upload in validation.get(protocol).get("upload"))): - module.fail_json(msg="Specified resource '" + upload + "' can't be uploaded to '" + protocol + "'") - if(download): - if(not (download in validation.get(protocol).get("download"))): - module.fail_json(msg="Specified resource '" + download + "' can't be downloaded from '" + protocol + "'") - - -def main(): - """entry point for module execution - """ - argument_spec = dict( - upload=dict( - type='str', - required=False, - choices=[ - 'running-config', - 'flash_primary', - 'flash_secondary', - 'startup-config']), - download=dict( - type='str', - required=False, - choices=[ - 'running-config', - 'startup-config', - 'flash_primary', - 'flash_secondary', - 'bootrom', - 'fips-primary-sig', - 'fips-secondary-sig', - 'fips-bootrom-sig']), - protocol=dict( - type='str', - required=True, - choices=[ - 'https', - 'scp']), - remote_server=dict( - type='str', - required=True), - remote_port=dict( - type='str', - required=False), - remote_filename=dict( - type='str', - required=True), - remote_user=dict( - type='str', - required=False), - remote_pass=dict( - type='str', - required=False, - no_log=True), - public_key=dict( - type='str', - required=False, - choices=[ - 'rsa', - 'dsa'])) - mutually_exclusive = [['upload', 'download']] - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, mutually_exclusive=mutually_exclusive) - - checkValidations(module) - warnings = list() - result = {'changed': False, 'warnings': warnings} - exec_command(module, 'skip') - - response = '' - try: - command = map_params_to_obj(module) - result['commands'] = [command["command"]] - - if(module.params['protocol'] == 'scp'): - response = exec_scp(module, command) - else: - response = run_commands(module, command) - if('Response Code: 404' in response): - module.fail_json(msg=response) - else: - result['response'] = "in progress..." - if(module.params["download"] is not None): - result['changed'] = True - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/network/icx/icx_facts.py b/lib/ansible/modules/network/icx/icx_facts.py deleted file mode 100644 index d2cf9fbc97..0000000000 --- a/lib/ansible/modules/network/icx/icx_facts.py +++ /dev/null @@ -1,549 +0,0 @@ -#!/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': 'community'} - -DOCUMENTATION = """ ---- -module: icx_facts -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Collect facts from remote Ruckus ICX 7000 series switches -description: - - Collects a base set of device facts from a remote device that - is running ICX. This module prepends all of the - base network fact keys with C(ansible_net_<fact>). The facts - module will always collect a base set of facts from the device - and can enable or disable collection of additional facts. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, hardware, config, and interfaces. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - type: list - default: '!config' -""" - -EXAMPLES = """ -# Collect all facts from the device -- icx_facts: - gather_subset: all - -# Collect only the config and default facts -- icx_facts: - gather_subset: - - config - -# Do not collect hardware facts -- icx_facts: - gather_subset: - - "!hardware" -""" - -RETURN = """ -ansible_net_gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list - -# default -ansible_net_model: - description: The model name returned from the device - returned: always - type: str -ansible_net_serialnum: - description: The serial number of the remote device - returned: always - type: str -ansible_net_version: - description: The operating system version running on the remote device - returned: always - type: str -ansible_net_hostname: - description: The configured hostname of the device - returned: always - type: str -ansible_net_image: - description: The image file the device is running - returned: always - type: str -ansible_net_stacked_models: - description: The model names of each device in the stack - returned: when multiple devices are configured in a stack - type: list -ansible_net_stacked_serialnums: - description: The serial numbers of each device in the stack - returned: when multiple devices are configured in a stack - type: list - -# hardware -ansible_net_filesystems: - description: All file system names available on the device - returned: when hardware is configured - type: list -ansible_net_filesystems_info: - description: A hash of all file systems containing info about each file system (e.g. free and total space) - returned: when hardware is configured - type: dict -ansible_net_memfree_mb: - description: The available free memory on the remote device in Mb - returned: when hardware is configured - type: int -ansible_net_memtotal_mb: - description: The total memory on the remote device in Mb - returned: when hardware is configured - type: int - -# config -ansible_net_config: - description: The current active config from the device - returned: when config is configured - type: str - -# interfaces -ansible_net_all_ipv4_addresses: - description: All IPv4 addresses configured on the device - returned: when interfaces is configured - type: list -ansible_net_all_ipv6_addresses: - description: All IPv6 addresses configured on the device - returned: when interfaces is configured - type: list -ansible_net_interfaces: - description: A hash of all interfaces running on the system - returned: when interfaces is configured - type: dict -ansible_net_neighbors: - description: The list of LLDP neighbors from the remote device - returned: when interfaces is configured - type: dict -""" - - -import re -from ansible.module_utils.network.icx.icx import run_commands -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible.module_utils.six.moves import zip - - -class FactsBase(object): - - COMMANDS = list() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, commands=self.COMMANDS, check_rc=False) - - def run(self, cmd): - return run_commands(self.module, commands=cmd, check_rc=False) - - -class Default(FactsBase): - - COMMANDS = ['show version', 'show running-config | include hostname'] - - def populate(self): - super(Default, self).run(['skip']) - super(Default, self).populate() - data = self.responses[0] - if data: - self.facts['version'] = self.parse_version(data) - self.facts['serialnum'] = self.parse_serialnum(data) - self.facts['model'] = self.parse_model(data) - self.facts['image'] = self.parse_image(data) - self.facts['hostname'] = self.parse_hostname(self.responses[1]) - self.parse_stacks(data) - - def parse_version(self, data): - match = re.search(r'SW: Version ([0-9]+.[0-9]+.[0-9a-zA-Z]+)', data) - if match: - return match.group(1) - - def parse_hostname(self, data): - match = re.search(r'^hostname (\S+)', data, re.M) - if match: - return match.group(1) - - def parse_model(self, data): - match = re.search(r'HW: (\S+ \S+)', data, re.M) - if match: - return match.group(1) - - def parse_image(self, data): - match = re.search(r'\([0-9]+ bytes\) from \S+ (\S+)', data) - if match: - return match.group(1) - - def parse_serialnum(self, data): - match = re.search(r'Serial #:(\S+)', data) - if match: - return match.group(1) - - def parse_stacks(self, data): - match = re.findall(r'UNIT [1-9]+: SL [1-9]+: (\S+)', data, re.M) - if match: - self.facts['stacked_models'] = match - - match = re.findall(r'^System [Ss]erial [Nn]umber\s+: (\S+)', data, re.M) - if match: - self.facts['stacked_serialnums'] = match - - -class Hardware(FactsBase): - - COMMANDS = [ - 'show memory', - 'show flash' - ] - - def populate(self): - super(Hardware, self).populate() - data = self.responses[0] - if data: - self.facts['filesystems'] = self.parse_filesystems(data) - self.facts['filesystems_info'] = self.parse_filesystems_info(self.responses[1]) - - if data: - if 'Invalid input detected' in data: - warnings.append('Unable to gather memory statistics') - else: - match = re.search(r'Dynamic memory: ([0-9]+) bytes total, ([0-9]+) bytes free, ([0-9]+%) used', data) - if match: - self.facts['memtotal_mb'] = int(match.group(1)) / 1024 - self.facts['memfree_mb'] = int(match.group(2)) / 1024 - - def parse_filesystems(self, data): - return "flash" - - def parse_filesystems_info(self, data): - facts = dict() - fs = '' - for line in data.split('\n'): - match = re.match(r'^(Stack unit \S+):', line) - if match: - fs = match.group(1) - facts[fs] = dict() - continue - match = re.match(r'\W+NAND Type: Micron NAND (\S+)', line) - if match: - facts[fs]['spacetotal'] = match.group(1) - match = re.match(r'\W+Code Flash Free Space = (\S+)', line) - if match: - facts[fs]['spacefree'] = int(int(match.group(1)) / 1024) - facts[fs]['spacefree'] = str(facts[fs]['spacefree']) + "Kb" - return {"flash": facts} - - -class Config(FactsBase): - - COMMANDS = ['skip', 'show running-config'] - - def populate(self): - super(Config, self).populate() - data = self.responses[1] - if data: - self.facts['config'] = data - - -class Interfaces(FactsBase): - - COMMANDS = [ - 'skip', - 'show interfaces', - 'show running-config', - 'show lldp', - 'show media' - ] - - def populate(self): - super(Interfaces, self).populate() - - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - data = self.responses[1] - if data: - interfaces = self.parse_interfaces(data) - self.facts['interfaces'] = self.populate_interfaces(interfaces) - - data = self.responses[1] - if data: - data = self.parse_interfaces(data) - self.populate_ipv4_interfaces(data) - - data = self.responses[2] - if data: - self.populate_ipv6_interfaces(data) - - data = self.responses[3] - lldp_errs = ['Invalid input', 'LLDP is not enabled'] - - if data and not any(err in data for err in lldp_errs): - neighbors = self.run(['show lldp neighbors detail']) - if neighbors: - self.facts['neighbors'] = self.parse_neighbors(neighbors[0]) - - data = self.responses[4] - self.populate_mediatype(data) - - interfaceList = {} - for iface in self.facts['interfaces']: - if 'type' in self.facts['interfaces'][iface]: - newName = self.facts['interfaces'][iface]['type'] + iface - else: - newName = iface - interfaceList[newName] = self.facts['interfaces'][iface] - self.facts['interfaces'] = interfaceList - - def populate_mediatype(self, data): - lines = data.split("\n") - for line in lines: - match = re.match(r'Port (\S+):\W+Type\W+:\W+(.*)', line) - if match: - self.facts['interfaces'][match.group(1)]["mediatype"] = match.group(2) - - def populate_interfaces(self, interfaces): - facts = dict() - for key, value in iteritems(interfaces): - intf = dict() - intf['description'] = self.parse_description(value) - intf['macaddress'] = self.parse_macaddress(value) - intf['mtu'] = self.parse_mtu(value) - intf['bandwidth'] = self.parse_bandwidth(value) - intf['duplex'] = self.parse_duplex(value) - intf['lineprotocol'] = self.parse_lineprotocol(value) - intf['operstatus'] = self.parse_operstatus(value) - intf['type'] = self.parse_type(value) - facts[key] = intf - return facts - - def populate_ipv4_interfaces(self, data): - for key, value in data.items(): - self.facts['interfaces'][key]['ipv4'] = dict() - primary_address = addresses = [] - primary_address = re.findall(r'Internet address is (\S+/\S+), .*$', value, re.M) - addresses = re.findall(r'Secondary address (.+)$', value, re.M) - if len(primary_address) == 0: - continue - addresses.append(primary_address[0]) - for address in addresses: - addr, subnet = address.split("/") - ipv4 = dict(address=addr.strip(), subnet=subnet.strip()) - self.add_ip_address(addr.strip(), 'ipv4') - self.facts['interfaces'][key]['ipv4'] = ipv4 - - def populate_ipv6_interfaces(self, data): - parts = data.split("\n") - for line in parts: - match = re.match(r'\W*interface \S+ (\S+)', line) - if match: - key = match.group(1) - try: - self.facts['interfaces'][key]['ipv6'] = list() - except KeyError: - self.facts['interfaces'][key] = dict() - self.facts['interfaces'][key]['ipv6'] = list() - self.facts['interfaces'][key]['ipv6'] = {} - continue - match = re.match(r'\W+ipv6 address (\S+)/(\S+)', line) - if match: - self.add_ip_address(match.group(1), "ipv6") - self.facts['interfaces'][key]['ipv6']["address"] = match.group(1) - self.facts['interfaces'][key]['ipv6']["subnet"] = match.group(2) - - def add_ip_address(self, address, family): - if family == 'ipv4': - self.facts['all_ipv4_addresses'].append(address) - else: - self.facts['all_ipv6_addresses'].append(address) - - def parse_neighbors(self, neighbors): - facts = dict() - for entry in neighbors.split('------------------------------------------------'): - if entry == '': - continue - intf = self.parse_lldp_intf(entry) - if intf not in facts: - facts[intf] = list() - fact = dict() - fact['host'] = self.parse_lldp_host(entry) - fact['port'] = self.parse_lldp_port(entry) - facts[intf].append(fact) - return facts - - def parse_interfaces(self, data): - parsed = dict() - key = '' - for line in data.split('\n'): - if len(line) == 0: - continue - elif line[0] == ' ': - parsed[key] += '\n%s' % line - else: - match = re.match(r'\S+Ethernet(\S+)', line) - if match: - key = match.group(1) - parsed[key] = line - return parsed - - def parse_description(self, data): - match = re.search(r'Port name is ([ \S]+)', data, re.M) - if match: - return match.group(1) - - def parse_macaddress(self, data): - match = re.search(r'Hardware is \S+, address is (\S+)', data) - if match: - return match.group(1) - - def parse_ipv4(self, data): - match = re.search(r'Internet address is (\S+)', data) - if match: - addr, masklen = match.group(1).split('/') - return dict(address=addr, masklen=int(masklen)) - - def parse_mtu(self, data): - match = re.search(r'MTU (\d+)', data) - if match: - return int(match.group(1)) - - def parse_bandwidth(self, data): - match = re.search(r'Configured speed (\S+), actual (\S+)', data) - if match: - return match.group(1) - - def parse_duplex(self, data): - match = re.search(r'configured duplex (\S+), actual (\S+)', data, re.M) - if match: - return match.group(2) - - def parse_mediatype(self, data): - match = re.search(r'media type is (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_type(self, data): - match = re.search(r'Hardware is (.+),', data, re.M) - if match: - return match.group(1) - - def parse_lineprotocol(self, data): - match = re.search(r'line protocol is (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_operstatus(self, data): - match = re.search(r'^(?:.+) is (.+),', data, re.M) - if match: - return match.group(1) - - def parse_lldp_intf(self, data): - match = re.search(r'^Local Intf: (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_lldp_host(self, data): - match = re.search(r'System Name: (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_lldp_port(self, data): - match = re.search(r'Port id: (.+)$', data, re.M) - if match: - return match.group(1) - - -FACT_SUBSETS = dict( - default=Default, - hardware=Hardware, - interfaces=Interfaces, - config=Config, -) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - -warnings = list() - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - gather_subset=dict(default=['!config'], type='list') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - gather_subset = module.params['gather_subset'] - - runable_subsets = set() - exclude_subsets = set() - - for subset in gather_subset: - if subset == 'all': - runable_subsets.update(VALID_SUBSETS) - continue - - if subset.startswith('!'): - subset = subset[1:] - if subset == 'all': - exclude_subsets.update(VALID_SUBSETS) - continue - exclude = True - else: - exclude = False - - if subset not in VALID_SUBSETS: - module.fail_json(msg='Bad subset') - - if exclude: - exclude_subsets.add(subset) - else: - runable_subsets.add(subset) - - if not runable_subsets: - runable_subsets.update(VALID_SUBSETS) - - runable_subsets.difference_update(exclude_subsets) - runable_subsets.add('default') - - facts = dict() - facts['gather_subset'] = list(runable_subsets) - - instances = list() - for key in runable_subsets: - instances.append(FACT_SUBSETS[key](module)) - - for inst in instances: - inst.populate() - facts.update(inst.facts) - - ansible_facts = dict() - for key, value in iteritems(facts): - key = 'ansible_net_%s' % key - ansible_facts[key] = value - - module.exit_json(ansible_facts=ansible_facts, warnings=warnings) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/network/icx/icx_interface.py b/lib/ansible/modules/network/icx/icx_interface.py deleted file mode 100644 index 252044aaff..0000000000 --- a/lib/ansible/modules/network/icx/icx_interface.py +++ /dev/null @@ -1,694 +0,0 @@ -#!/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': 'community'} - - -DOCUMENTATION = """ ---- -module: icx_interface -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Manage Interface on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of Interfaces - on ruckus icx devices. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - name: - description: - - Name of the Interface. - type: str - description: - description: - - Name of the description. - type: str - enabled: - description: - - Interface link status - default: yes - type: bool - speed: - description: - - Interface link speed/duplex - choices: ['10-full', '10-half', '100-full', '100-half', '1000-full', '1000-full-master', - '1000-full-slave', '10g-full', '10g-full-master', '10g-full-slave', '2500-full', '2500-full-master', - '2500-full-slave', '5g-full', '5g-full-master', '5g-full-slave', 'auto'] - type: str - stp: - description: - - enable/disable stp for the interface - type: bool - tx_rate: - description: - - Transmit rate in bits per second (bps). - - This is state check parameter only. - - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) - type: str - rx_rate: - description: - - Receiver rate in bits per second (bps). - - This is state check parameter only. - - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) - type: str - neighbors: - description: - - Check the operational state of given interface C(name) for CDP/LLDP neighbor. - - The following suboptions are available. - type: list - suboptions: - host: - description: - - "CDP/LLDP neighbor host for given interface C(name)." - type: str - port: - description: - - "CDP/LLDP neighbor port to which given interface C(name) is connected." - type: str - delay: - description: - - Time in seconds to wait before checking for the operational state on remote - device. This wait is applicable for operational state argument which are - I(state) with values C(up)/C(down), I(tx_rate) and I(rx_rate). - type: int - default: 10 - state: - description: - - State of the Interface configuration, C(up) means present and - operationally up and C(down) means present and operationally C(down) - default: present - type: str - choices: ['present', 'absent', 'up', 'down'] - power: - description: - - Inline power on Power over Ethernet (PoE) ports. - type: dict - suboptions: - by_class: - description: - - "The range is 0-4" - - "The power limit based on class value for given interface C(name)" - choices: ['0', '1', '2', '3', '4'] - type: str - limit: - description: - - "The range is 1000-15400|30000mW. For PoH ports the range is 1000-95000mW" - - "The power limit based on actual power value for given interface C(name)" - type: str - priority: - description: - - "The range is 1 (highest) to 3 (lowest)" - - "The priority for power management or given interface C(name)" - choices: ['1', '2', '3'] - type: str - enabled: - description: - - "enable/disable the poe of the given interface C(name)" - default: no - type: bool - aggregate: - description: - - List of Interfaces definitions. - type: list - suboptions: - name: - description: - - Name of the Interface. - type: str - description: - description: - - Name of the description. - type: str - enabled: - description: - - Interface link status - type: bool - speed: - description: - - Interface link speed/duplex - choices: ['10-full', '10-half', '100-full', '100-half', '1000-full', '1000-full-master', - '1000-full-slave', '10g-full', '10g-full-master', '10g-full-slave', '2500-full', '2500-full-master', - '2500-full-slave', '5g-full', '5g-full-master', '5g-full-slave', 'auto'] - type: str - stp: - description: - - enable/disable stp for the interface - type: bool - tx_rate: - description: - - Transmit rate in bits per second (bps). - - This is state check parameter only. - - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) - type: str - rx_rate: - description: - - Receiver rate in bits per second (bps). - - This is state check parameter only. - - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) - type: str - neighbors: - description: - - Check the operational state of given interface C(name) for CDP/LLDP neighbor. - - The following suboptions are available. - type: list - suboptions: - host: - description: - - "CDP/LLDP neighbor host for given interface C(name)." - type: str - port: - description: - - "CDP/LLDP neighbor port to which given interface C(name) is connected." - type: str - delay: - description: - - Time in seconds to wait before checking for the operational state on remote - device. This wait is applicable for operational state argument which are - I(state) with values C(up)/C(down), I(tx_rate) and I(rx_rate). - type: int - state: - description: - - State of the Interface configuration, C(up) means present and - operationally up and C(down) means present and operationally C(down) - type: str - choices: ['present', 'absent', 'up', 'down'] - 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 overridden, by specifying it as module parameter. - type: bool - power: - description: - - Inline power on Power over Ethernet (PoE) ports. - type: dict - suboptions: - by_class: - description: - - "The range is 0-4" - - "The power limit based on class value for given interface C(name)" - choices: ['0', '1', '2', '3', '4'] - type: str - limit: - description: - - "The range is 1000-15400|30000mW. For PoH ports the range is 1000-95000mW" - - "The power limit based on actual power value for given interface C(name)" - type: str - priority: - description: - - "The range is 1 (highest) to 3 (lowest)" - - "The priority for power management or given interface C(name)" - choices: ['1', '2', '3'] - type: str - enabled: - description: - - "enable/disable the poe of the given interface C(name)" - type: bool - 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 overridden, - by specifying it as module parameter. - default: yes - type: bool -""" - -EXAMPLES = """ -- name: enable ethernet port and set name - icx_interface: - name: ethernet 1/1/1 - description: interface-1 - stp: true - enabled: true - -- name: disable ethernet port 1/1/1 - icx_interface: - name: ethernet 1/1/1 - enabled: false - -- name: enable ethernet port range, set name and speed. - icx_interface: - name: ethernet 1/1/1 to 1/1/10 - description: interface-1 - speed: 100-full - enabled: true - -- name: enable poe. Set class. - icx_interface: - name: ethernet 1/1/1 - power: - by_class: 2 - -- name: configure poe limit of interface - icx_interface: - name: ethernet 1/1/1 - power: - limit: 10000 - -- name: disable poe of interface - icx_interface: - name: ethernet 1/1/1 - power: - enabled: false - -- name: set lag name for a range of lags - icx_interface: - name: lag 1 to 10 - description: test lags - -- name: Disable lag - icx_interface: - name: lag 1 - enabled: false - -- name: enable management interface - icx_interface: - name: management 1 - enabled: true - -- name: enable loopback interface - icx_interface: - name: loopback 10 - enabled: true - -- name: Add interface using aggregate - icx_interface: - aggregate: - - { name: ethernet 1/1/1, description: test-interface-1, power: { by_class: 2 } } - - { name: ethernet 1/1/3, description: test-interface-3} - speed: 10-full - enabled: true - -- name: Check tx_rate, rx_rate intent arguments - icx_interface: - name: ethernet 1/1/10 - state: up - tx_rate: ge(0) - rx_rate: le(0) - -- name: Check neighbors intent arguments - icx_interface: - name: ethernet 1/1/10 - neighbors: - - port: 1/1/5 - host: netdev -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - interface ethernet 1/1/1 - - port-name interface-1 - - state present - - speed-duplex 100-full - - inline power priority 1 -""" - -import re -from copy import deepcopy -from time import sleep -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.network.common.config import NetworkConfig -from ansible.module_utils.network.icx.icx import load_config, get_config -from ansible.module_utils.connection import Connection, ConnectionError, exec_command -from ansible.module_utils.network.common.utils import conditional, remove_default_spec - - -def parse_enable(configobj, name): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - match = re.search(r'^disable', cfg, re.M) - if match: - return True - else: - return False - - -def parse_power_argument(configobj, name): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - match = re.search(r'(^inline power|^inline power(.*))+$', cfg, re.M) - if match: - return match.group(1) - - -def parse_config_argument(configobj, name, arg=None): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - match = re.search(r'%s (.+)$' % arg, cfg, re.M) - if match: - return match.group(1) - - -def parse_stp_arguments(module, item): - rc, out, err = exec_command(module, 'show interfaces ' + item) - match = re.search(r'STP configured to (\S+),', out, re.S) - if match: - return True if match.group(1) == "ON" else False - - -def search_obj_in_list(name, lst): - for o in lst: - if o['name'] == name: - return o - - return None - - -def validate_power(module, power): - count = 0 - for item in power: - if power.get(item) is not None: - count += 1 - if count > 1: - module.fail_json(msg='power parameters are mutually exclusive: class,limit,priority,enabled') - - -def add_command_to_interface(interface, cmd, commands): - if interface not in commands: - commands.append(interface) - commands.append(cmd) - - -def map_config_to_obj(module): - compare = module.params['check_running_config'] - config = get_config(module, None, compare) - configobj = NetworkConfig(indent=1, contents=config) - match = re.findall(r'^interface (.+)$', config, re.M) - - if not match: - return list() - - instances = list() - - for item in set(match): - obj = { - 'name': item, - 'port-name': parse_config_argument(configobj, item, 'port-name'), - 'speed-duplex': parse_config_argument(configobj, item, 'speed-duplex'), - 'stp': parse_stp_arguments(module, item), - 'disable': True if parse_enable(configobj, item) else False, - 'power': parse_power_argument(configobj, item), - 'state': 'present' - } - instances.append(obj) - return instances - - -def parse_poe_config(poe, power): - if poe.get('by_class') is not None: - power += 'power-by-class %s' % poe.get('by_class') - elif poe.get('limit') is not None: - power += 'power-limit %s' % poe.get('limit') - elif poe.get('priority') is not None: - power += 'priority %s' % poe.get('priority') - elif poe.get('enabled'): - power = 'inline power' - elif poe.get('enabled') is False: - power = 'no inline power' - return power - - -def map_params_to_obj(module): - obj = [] - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - item['port-name'] = item.pop('description') - item['speed-duplex'] = item.pop('speed') - poe = item.get('power') - if poe: - - validate_power(module, poe) - power = 'inline power' + ' ' - power_arg = parse_poe_config(poe, power) - item.update({'power': power_arg}) - - d = item.copy() - - if d['enabled']: - d['disable'] = False - else: - d['disable'] = True - - obj.append(d) - - else: - params = { - 'name': module.params['name'], - 'port-name': module.params['description'], - 'speed-duplex': module.params['speed'], - 'stp': module.params['stp'], - 'delay': module.params['delay'], - 'state': module.params['state'], - 'tx_rate': module.params['tx_rate'], - 'rx_rate': module.params['rx_rate'], - 'neighbors': module.params['neighbors'] - } - poe = module.params.get('power') - if poe: - validate_power(module, poe) - power = 'inline power' + ' ' - power_arg = parse_poe_config(poe, power) - params.update({'power': power_arg}) - - if module.params['enabled']: - params.update({'disable': False}) - else: - params.update({'disable': True}) - - obj.append(params) - return obj - - -def map_obj_to_commands(updates): - commands = list() - want, have = updates - - args = ('speed-duplex', 'port-name', 'power', 'stp') - for w in want: - name = w['name'] - disable = w['disable'] - state = w['state'] - - obj_in_have = search_obj_in_list(name, have) - interface = 'interface ' + name - - if state == 'absent' and have == []: - commands.append('no ' + interface) - - elif state == 'absent' and obj_in_have: - commands.append('no ' + interface) - - elif state in ('present', 'up', 'down'): - if obj_in_have: - for item in args: - candidate = w.get(item) - running = obj_in_have.get(item) - if candidate == 'no inline power' and running is None: - candidate = None - if candidate != running: - if candidate: - if item == 'power': - cmd = str(candidate) - elif item == 'stp': - cmd = 'spanning-tree' if candidate else 'no spanning-tree' - else: - cmd = item + ' ' + str(candidate) - add_command_to_interface(interface, cmd, commands) - - if disable and not obj_in_have.get('disable', False): - add_command_to_interface(interface, 'disable', commands) - elif not disable and obj_in_have.get('disable', False): - add_command_to_interface(interface, 'enable', commands) - else: - commands.append(interface) - for item in args: - value = w.get(item) - if value: - if item == 'power': - commands.append(str(value)) - elif item == 'stp': - cmd = 'spanning-tree' if item else 'no spanning-tree' - else: - commands.append(item + ' ' + str(value)) - - if disable: - commands.append('disable') - if disable is False: - commands.append('enable') - - return commands - - -def check_declarative_intent_params(module, want, result): - failed_conditions = [] - have_neighbors_lldp = None - have_neighbors_cdp = None - for w in want: - want_state = w.get('state') - want_tx_rate = w.get('tx_rate') - want_rx_rate = w.get('rx_rate') - want_neighbors = w.get('neighbors') - - if want_state not in ('up', 'down') and not want_tx_rate and not want_rx_rate and not want_neighbors: - continue - - if result['changed']: - sleep(w['delay']) - - command = 'show interfaces %s' % w['name'] - rc, out, err = exec_command(module, command) - - if rc != 0: - module.fail_json(msg=to_text(err, errors='surrogate_then_replace'), command=command, rc=rc) - - if want_state in ('up', 'down'): - match = re.search(r'%s (\w+)' % 'line protocol is', out, re.M) - have_state = None - if match: - have_state = match.group(1) - if have_state is None or not conditional(want_state, have_state.strip()): - failed_conditions.append('state ' + 'eq(%s)' % want_state) - - if want_tx_rate: - match = re.search(r'%s (\d+)' % 'output rate:', out, re.M) - have_tx_rate = None - if match: - have_tx_rate = match.group(1) - - if have_tx_rate is None or not conditional(want_tx_rate, have_tx_rate.strip(), cast=int): - failed_conditions.append('tx_rate ' + want_tx_rate) - - if want_rx_rate: - match = re.search(r'%s (\d+)' % 'input rate:', out, re.M) - have_rx_rate = None - if match: - have_rx_rate = match.group(1) - - if have_rx_rate is None or not conditional(want_rx_rate, have_rx_rate.strip(), cast=int): - failed_conditions.append('rx_rate ' + want_rx_rate) - - if want_neighbors: - have_host = [] - have_port = [] - - if have_neighbors_lldp is None: - rc, have_neighbors_lldp, err = exec_command(module, 'show lldp neighbors detail') - if rc != 0: - module.fail_json(msg=to_text(err, errors='surrogate_then_replace'), command=command, rc=rc) - if have_neighbors_lldp: - lines = have_neighbors_lldp.strip().split('Local port: ') - - for line in lines: - field = line.split('\n') - if field[0].strip() == w['name'].split(' ')[1]: - for item in field: - match = re.search(r'\s*\+\s+System name\s+:\s+"(.*)"', item, re.M) - if match: - have_host.append(match.group(1)) - - match = re.search(r'\s*\+\s+Port description\s+:\s+"(.*)"', item, re.M) - if match: - have_port.append(match.group(1)) - - for item in want_neighbors: - host = item.get('host') - port = item.get('port') - if host and host not in have_host: - failed_conditions.append('host ' + host) - if port and port not in have_port: - failed_conditions.append('port ' + port) - return failed_conditions - - -def main(): - """ main entry point for module execution - """ - power_spec = dict( - by_class=dict(choices=['0', '1', '2', '3', '4']), - limit=dict(type='str'), - priority=dict(choices=['1', '2', '3']), - enabled=dict(type='bool') - ) - neighbors_spec = dict( - host=dict(), - port=dict() - ) - element_spec = dict( - name=dict(), - description=dict(), - enabled=dict(default=True, type='bool'), - speed=dict(type='str', choices=['10-full', '10-half', '100-full', '100-half', '1000-full', '1000-full-master', - '1000-full-slave', '10g-full', '10g-full-master', '10g-full-slave', '2500-full', '2500-full-master', - '2500-full-slave', '5g-full', '5g-full-master', '5g-full-slave', 'auto']), - stp=dict(type='bool'), - tx_rate=dict(), - rx_rate=dict(), - neighbors=dict(type='list', elements='dict', options=neighbors_spec), - delay=dict(default=10, type='int'), - state=dict(default='present', - choices=['present', 'absent', 'up', 'down']), - power=dict(type='dict', options=power_spec), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - ) - argument_spec.update(element_spec) - - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - warnings = list() - result = {} - result['changed'] = False - if warnings: - result['warnings'] = warnings - exec_command(module, 'skip') - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands((want, have)) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - failed_conditions = check_declarative_intent_params(module, want, result) - - if failed_conditions: - msg = 'One or more conditional statements have not been satisfied' - module.fail_json(msg=msg, failed_conditions=failed_conditions) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/network/icx/icx_l3_interface.py b/lib/ansible/modules/network/icx/icx_l3_interface.py deleted file mode 100644 index eb3f127aa3..0000000000 --- a/lib/ansible/modules/network/icx/icx_l3_interface.py +++ /dev/null @@ -1,439 +0,0 @@ -#!/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': 'community'} - -DOCUMENTATION = """ ---- -module: icx_l3_interface -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Manage Layer-3 interfaces on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of Layer-3 interfaces - on ICX network devices. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - name: - description: - - Name of the Layer-3 interface to be configured eg. GigabitEthernet0/2, ve 10, ethernet 1/1/1 - type: str - ipv4: - description: - - IPv4 address to be set for the Layer-3 interface mentioned in I(name) option. - The address format is <ipv4 address>/<mask>, the mask is number - in range 0-32 eg. 192.168.0.1/24 - type: str - ipv6: - description: - - IPv6 address to be set for the Layer-3 interface mentioned in I(name) option. - The address format is <ipv6 address>/<mask>, the mask is number - in range 0-128 eg. fd5d:12c9:2201:1::1/64. - type: str - mode: - description: - - Specifies if ipv4 address should be dynamic/advertise to ospf/not advertise to ospf. - This should be specified only if ipv4 address is configured and if it is not secondary IP address. - choices: ['dynamic', 'ospf-ignore', 'ospf-passive'] - type: str - replace: - description: - - Replaces the configured primary IP address on the interface. - choices: ['yes', 'no'] - type: str - secondary: - description: - - Specifies that the configured address is a secondary IP address. - If this keyword is omitted, the configured address is the primary IP address. - choices: ['yes', 'no'] - type: str - aggregate: - description: - - List of Layer-3 interfaces definitions. Each of the entry in aggregate list should - define name of interface C(name) and a optional C(ipv4) or C(ipv6) address. - type: list - suboptions: - name: - description: - - Name of the Layer-3 interface to be configured eg. GigabitEthernet0/2, ve 10, ethernet 1/1/1 - type: str - ipv4: - description: - - IPv4 address to be set for the Layer-3 interface mentioned in I(name) option. - The address format is <ipv4 address>/<mask>, the mask is number - in range 0-32 eg. 192.168.0.1/24 - type: str - ipv6: - description: - - IPv6 address to be set for the Layer-3 interface mentioned in I(name) option. - The address format is <ipv6 address>/<mask>, the mask is number - in range 0-128 eg. fd5d:12c9:2201:1::1/64. - type: str - mode: - description: - - Specifies if ipv4 address should be dynamic/advertise to ospf/not advertise to ospf. - This should be specified only if ipv4 address is configured and if it is not secondary IP address. - choices: ['dynamic', 'ospf-ignore', 'ospf-passive'] - type: str - replace: - description: - - Replaces the configured primary IP address on the interface. - choices: ['yes', 'no'] - type: str - secondary: - description: - - Specifies that the configured address is a secondary IP address. - If this keyword is omitted, the configured address is the primary IP address. - choices: ['yes', 'no'] - type: str - state: - description: - - State of the Layer-3 interface configuration. It indicates if the configuration should - be present or absent on remote device. - choices: ['present', 'absent'] - type: str - 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 overridden, by specifying it as module parameter. - type: bool - state: - description: - - State of the Layer-3 interface configuration. It indicates if the configuration should - be present or absent on remote device. - default: present - choices: ['present', 'absent'] - type: str - 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 overridden, by specifying it as module parameter. - type: bool - default: yes -""" - -EXAMPLES = """ -- name: Remove ethernet 1/1/1 IPv4 and IPv6 address - icx_l3_interface: - name: ethernet 1/1/1 - ipv4: 192.168.0.1/24 - ipv6: "fd5d:12c9:2201:1::1/64" - state: absent - -- name: Replace ethernet 1/1/1 primary IPv4 address - icx_l3_interface: - name: ethernet 1/1/1 - ipv4: 192.168.0.1/24 - replace: yes - state: absent - -- name: Replace ethernet 1/1/1 dynamic IPv4 address - icx_l3_interface: - name: ethernet 1/1/1 - ipv4: 192.168.0.1/24 - mode: dynamic - state: absent - -- name: Set ethernet 1/1/1 secondary IPv4 address - icx_l3_interface: - name: ethernet 1/1/1 - ipv4: 192.168.0.1/24 - secondary: yes - state: absent - -- name: Set ethernet 1/1/1 IPv4 address - icx_l3_interface: - name: ethernet 1/1/1 - ipv4: 192.168.0.1/24 - -- name: Set ethernet 1/1/1 IPv6 address - icx_l3_interface: - name: ethernet 1/1/1 - ipv6: "fd5d:12c9:2201:1::1/64" - -- name: Set IP addresses on aggregate - icx_l3_interface: - aggregate: - - { name: GigabitEthernet0/3, ipv4: 192.168.2.10/24 } - - { name: GigabitEthernet0/3, ipv4: 192.168.3.10/24, ipv6: "fd5d:12c9:2201:1::1/64" } - -- name: Remove IP addresses on aggregate - icx_l3_interface: - aggregate: - - { name: GigabitEthernet0/3, ipv4: 192.168.2.10/24 } - - { name: GigabitEthernet0/3, ipv4: 192.168.3.10/24, ipv6: "fd5d:12c9:2201:1::1/64" } - state: absent - - -- name: Set the ipv4 and ipv6 of a virtual ethernet(ve) - icx_l3_interface: - name: ve 100 - ipv4: 192.168.0.1 - ipv6: "2001:0db8:85a3:0000:0000:8a2e:0370:7334" -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - interface ethernet 1/1/1 - - ip address 192.168.0.1 255.255.255.0 - - ipv6 address fd5d:12c9:2201:1::1/64 -""" - - -import re -from copy import deepcopy -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import exec_command -from ansible.module_utils.network.icx.icx import get_config, load_config -from ansible.module_utils.network.common.config import NetworkConfig -from ansible.module_utils.network.common.utils import remove_default_spec -from ansible.module_utils.network.common.utils import is_netmask, is_masklen, to_netmask, to_masklen - - -def validate_ipv4(value, module): - if value: - address = value.split('/') - if len(address) != 2: - module.fail_json(msg='address format is <ipv4 address>/<mask>, got invalid format %s' % value) - else: - if not is_masklen(address[1]): - module.fail_json(msg='invalid value for mask: %s, mask should be in range 0-32' % address[1]) - - -def validate_ipv6(value, module): - if value: - address = value.split('/') - if len(address) != 2: - module.fail_json(msg='address format is <ipv6 address>/<mask>, got invalid format %s' % value) - else: - if not 0 <= int(address[1]) <= 128: - module.fail_json(msg='invalid value for mask: %s, mask should be in range 0-128' % address[1]) - - -def validate_param_values(module, obj, param=None): - if param is None: - param = module.params - for key in obj: - validator = globals().get('validate_%s' % key) - if callable(validator): - validator(param.get(key), module) - - -def map_params_to_obj(module): - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - validate_param_values(module, item, item) - obj.append(item.copy()) - else: - obj.append({ - 'name': module.params['name'], - 'ipv4': module.params['ipv4'], - 'ipv6': module.params['ipv6'], - 'state': module.params['state'], - 'replace': module.params['replace'], - 'mode': module.params['mode'], - 'secondary': module.params['secondary'], - }) - - validate_param_values(module, obj) - - return obj - - -def parse_config_argument(configobj, name, arg=None): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - - values = [] - matches = re.finditer(r'%s (.+)$' % arg, cfg, re.M) - for match in matches: - match_str = match.group(1).strip() - if arg == 'ipv6 address': - values.append(match_str) - else: - values = match_str - break - - return values or None - - -def search_obj_in_list(name, lst): - for o in lst: - if o['name'] == name: - return o - - return None - - -def map_config_to_obj(module): - compare = module.params['check_running_config'] - config = get_config(module, flags=['| begin interface'], compare=compare) - configobj = NetworkConfig(indent=1, contents=config) - - match = re.findall(r'^interface (\S+ \S+)', config, re.M) - if not match: - return list() - - instances = list() - - for item in set(match): - ipv4 = parse_config_argument(configobj, item, 'ip address') - if ipv4: - address = ipv4.strip().split(' ') - if len(address) == 2 and is_netmask(address[1]): - ipv4 = '{0}/{1}'.format(address[0], to_text(to_masklen(address[1]))) - obj = { - 'name': item, - 'ipv4': ipv4, - 'ipv6': parse_config_argument(configobj, item, 'ipv6 address'), - 'state': 'present' - } - instances.append(obj) - - return instances - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - for w in want: - name = w['name'] - ipv4 = w['ipv4'] - ipv6 = w['ipv6'] - state = w['state'] - if 'replace' in w: - replace = w['replace'] == 'yes' - else: - replace = False - if w['mode'] is not None: - mode = ' ' + w['mode'] - else: - mode = '' - if w['secondary'] is not None: - secondary = w['secondary'] == 'yes' - else: - secondary = False - - interface = 'interface ' + name - commands.append(interface) - - obj_in_have = search_obj_in_list(name, have) - if state == 'absent' and have == []: - if ipv4: - address = ipv4.split('/') - if len(address) == 2: - ipv4 = '{addr} {mask}'.format(addr=address[0], mask=to_netmask(address[1])) - commands.append('no ip address {ip}'.format(ip=ipv4)) - if ipv6: - commands.append('no ipv6 address {ip}'.format(ip=ipv6)) - - elif state == 'absent' and obj_in_have: - if obj_in_have['ipv4']: - if ipv4: - address = ipv4.split('/') - if len(address) == 2: - ipv4 = '{addr} {mask}'.format(addr=address[0], mask=to_netmask(address[1])) - commands.append('no ip address {ip}'.format(ip=ipv4)) - if obj_in_have['ipv6']: - if ipv6: - commands.append('no ipv6 address {ip}'.format(ip=ipv6)) - - elif state == 'present': - if ipv4: - if obj_in_have is None or obj_in_have.get('ipv4') is None or ipv4 != obj_in_have['ipv4']: - address = ipv4.split('/') - if len(address) == 2: - ipv4 = '{0} {1}'.format(address[0], to_netmask(address[1])) - commands.append('ip address %s%s%s%s' % (format(ipv4), mode, ' replace' if (replace) else '', ' secondary' if (secondary) else '')) - - if ipv6: - if obj_in_have is None or obj_in_have.get('ipv6') is None or ipv6.lower() not in [addr.lower() for addr in obj_in_have['ipv6']]: - commands.append('ipv6 address {ip}'.format(ip=ipv6)) - - if commands[-1] == interface: - commands.pop(-1) - else: - commands.append("exit") - - return commands - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - name=dict(), - ipv4=dict(), - ipv6=dict(), - replace=dict(choices=['yes', 'no']), - mode=dict(choices=['dynamic', 'ospf-ignore', 'ospf-passive']), - secondary=dict(choices=['yes', 'no']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])), - state=dict(default='present', - choices=['present', 'absent']), - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec) - ) - - argument_spec.update(element_spec) - - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate'], ['secondary', 'replace'], ['secondary', 'mode']] - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - warnings = list() - - result = {'changed': False} - exec_command(module, 'skip') - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands((want, have), module) - - if commands: - if not module.check_mode: - resp = load_config(module, commands) - warnings.extend((out for out in resp if out)) - - result['changed'] = True - - if warnings: - result['warnings'] = warnings - - result['commands'] = commands - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/network/icx/icx_linkagg.py b/lib/ansible/modules/network/icx/icx_linkagg.py deleted file mode 100644 index 5bca77ade5..0000000000 --- a/lib/ansible/modules/network/icx/icx_linkagg.py +++ /dev/null @@ -1,328 +0,0 @@ -#!/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': 'community'} - - -DOCUMENTATION = """ ---- -module: icx_linkagg -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Manage link aggregation groups on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of link aggregation groups - on Ruckus ICX network devices. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - group: - description: - - Channel-group number for the port-channel - Link aggregation group. Range 1-255 or set to 'auto' to auto-generates a LAG ID - type: int - name: - description: - - Name of the LAG - type: str - mode: - description: - - Mode of the link aggregation group. - type: str - choices: ['dynamic', 'static'] - members: - description: - - List of port members or ranges of the link aggregation group. - type: list - state: - description: - - State of the link aggregation group. - type: str - default: present - choices: ['present', 'absent'] - 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 overridden, by specifying it as module parameter. - type: bool - default: yes - aggregate: - description: - - List of link aggregation definitions. - type: list - suboptions: - group: - description: - - Channel-group number for the port-channel - Link aggregation group. Range 1-255 or set to 'auto' to auto-generates a LAG ID - type: int - name: - description: - - Name of the LAG - type: str - mode: - description: - - Mode of the link aggregation group. - type: str - choices: ['dynamic', 'static'] - members: - description: - - List of port members or ranges of the link aggregation group. - type: list - state: - description: - - State of the link aggregation group. - type: str - choices: ['present', 'absent'] - 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 overridden, by specifying it as module parameter. - type: bool - purge: - description: - - Purge links not defined in the I(aggregate) parameter. - type: bool - default: no - -""" - -EXAMPLES = """ -- name: create static link aggregation group - icx_linkagg: - group: 10 - mode: static - name: LAG1 - -- name: create link aggregation group with auto id - icx_linkagg: - group: auto - mode: dynamic - name: LAG2 - -- name: delete link aggregation group - icx_linkagg: - group: 10 - state: absent - -- name: Set members to LAG - icx_linkagg: - group: 200 - mode: static - members: - - ethernet 1/1/1 to 1/1/6 - - ethernet 1/1/10 - -- name: Remove links other then LAG id 100 and 3 using purge - icx_linkagg: - aggregate: - - { group: 3} - - { group: 100} - purge: true -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - lag LAG1 dynamic id 11 - - ports ethernet 1/1/1 to 1/1/6 - - no ports ethernet 1/1/10 - - no lag LAG1 dynamic id 12 -""" - - -import re -from copy import deepcopy - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import ConnectionError, exec_command -from ansible.module_utils.network.icx.icx import run_commands, get_config, load_config -from ansible.module_utils.network.common.config import CustomNetworkConfig -from ansible.module_utils.network.common.utils import remove_default_spec - - -def range_to_members(ranges, prefix=""): - match = re.findall(r'(ethe[a-z]* [0-9]/[0-9]/[0-9]+)( to [0-9]/[0-9]/[0-9]+)?', ranges) - members = list() - for m in match: - start, end = m - if(end == ''): - start = start.replace("ethe ", "ethernet ") - members.append("%s%s" % (prefix, start)) - else: - start_tmp = re.search(r'[0-9]/[0-9]/([0-9]+)', start) - end_tmp = re.search(r'[0-9]/[0-9]/([0-9]+)', end) - start = int(start_tmp.group(1)) - end = int(end_tmp.group(1)) + 1 - for num in range(start, end): - members.append("%sethernet 1/1/%s" % (prefix, num)) - return members - - -def map_config_to_obj(module): - objs = dict() - compare = module.params['check_running_config'] - config = get_config(module, None, compare=compare) - obj = None - for line in config.split('\n'): - l = line.strip() - match1 = re.search(r'lag (\S+) (\S+) id (\S+)', l, re.M) - if match1: - obj = dict() - obj['name'] = match1.group(1) - obj['mode'] = match1.group(2) - obj['group'] = match1.group(3) - obj['state'] = 'present' - obj['members'] = list() - else: - match2 = re.search(r'ports .*', l, re.M) - if match2 and obj is not None: - obj['members'].extend(range_to_members(match2.group(0))) - elif obj is not None: - objs[obj['group']] = obj - obj = None - return objs - - -def map_params_to_obj(module): - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - d = item.copy() - d['group'] = str(d['group']) - obj.append(d) - else: - obj.append({ - 'group': str(module.params['group']), - 'mode': module.params['mode'], - 'members': module.params['members'], - 'state': module.params['state'], - 'name': module.params['name'] - }) - - return obj - - -def search_obj_in_list(group, lst): - for o in lst: - if o['group'] == group: - return o - return None - - -def is_member(member, lst): - for li in lst: - ml = range_to_members(li) - if member in ml: - return True - return False - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - purge = module.params['purge'] - - for w in want: - if have == {} and w['state'] == 'absent': - commands.append("%slag %s %s id %s" % ('no ' if w['state'] == 'absent' else '', w['name'], w['mode'], w['group'])) - elif have.get(w['group']) is None: - commands.append("%slag %s %s id %s" % ('no ' if w['state'] == 'absent' else '', w['name'], w['mode'], w['group'])) - if(w.get('members') is not None and w['state'] == 'present'): - for m in w['members']: - commands.append("ports %s" % (m)) - if w['state'] == 'present': - commands.append("exit") - else: - commands.append("%slag %s %s id %s" % ('no ' if w['state'] == 'absent' else '', w['name'], w['mode'], w['group'])) - if(w.get('members') is not None and w['state'] == 'present'): - for m in have[w['group']]['members']: - if not is_member(m, w['members']): - commands.append("no ports %s" % (m)) - for m in w['members']: - sm = range_to_members(ranges=m) - for smm in sm: - if smm not in have[w['group']]['members']: - commands.append("ports %s" % (smm)) - - if w['state'] == 'present': - commands.append("exit") - if purge: - for h in have: - if search_obj_in_list(have[h]['group'], want) is None: - commands.append("no lag %s %s id %s" % (have[h]['name'], have[h]['mode'], have[h]['group'])) - return commands - - -def main(): - element_spec = dict( - group=dict(type='int'), - name=dict(type='str'), - mode=dict(choices=['dynamic', 'static']), - members=dict(type='list'), - state=dict(default='present', - choices=['present', 'absent']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['group'] = dict(required=True, type='int') - - required_one_of = [['group', 'aggregate']] - required_together = [['name', 'group']] - mutually_exclusive = [['group', 'aggregate']] - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec, required_together=required_together), - purge=dict(default=False, type='bool') - ) - - argument_spec.update(element_spec) - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - required_together=required_together, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - warnings = list() - result = {'changed': False} - exec_command(module, 'skip') - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands((want, have), module) - - result["commands"] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/network/icx/icx_lldp.py b/lib/ansible/modules/network/icx/icx_lldp.py deleted file mode 100644 index 972d4a5937..0000000000 --- a/lib/ansible/modules/network/icx/icx_lldp.py +++ /dev/null @@ -1,184 +0,0 @@ -#!/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': 'community'} - - -DOCUMENTATION = """ ---- -module: icx_lldp -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Manage LLDP configuration on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of LLDP service on ICX network devices. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - interfaces: - description: - - specify interfaces - suboptions: - name: - description: - - List of ethernet ports to enable lldp. To add a range of ports use 'to' keyword. See the example. - type: list - state: - description: - - State of lldp configuration for interfaces - type: str - choices: ['present', 'absent', 'enabled', 'disabled'] - type: list - 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 overridden, by specifying it as module parameter. - type: bool - default: yes - state: - description: - - Enables the receipt and transmission of Link Layer Discovery Protocol (LLDP) globally. - type: str - choices: ['present', 'absent', 'enabled', 'disabled'] -""" - -EXAMPLES = """ -- name: Disable LLDP - icx_lldp: - state: absent - -- name: Enable LLDP - icx_lldp: - state: present - -- name: Disable LLDP on ports 1/1/1 - 1/1/10, 1/1/20 - icx_lldp: - interfaces: - - name: - - ethernet 1/1/1 to 1/1/10 - - ethernet 1/1/20 - state: absent - state: present - -- name: Enable LLDP on ports 1/1/5 - 1/1/10 - icx_lldp: - interfaces: - - name: - - ethernet 1/1/1 to 1/1/10 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - lldp run - - no lldp run -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.network.icx.icx import load_config, run_commands - - -def has_lldp(module): - run_commands(module, ['skip']) - output = run_commands(module, ['show lldp']) - is_lldp_enable = False - if len(output) > 0 and "LLDP is not running" not in output[0]: - is_lldp_enable = True - - return is_lldp_enable - - -def map_obj_to_commands(module, commands): - interfaces = module.params.get('interfaces') - for item in interfaces: - state = item.get('state') - if state == 'present': - for port in item.get('name'): - if 'all' in port: - module.fail_json(msg='cannot enable on all the ports') - else: - commands.append('lldp enable ports {0}'.format(str(port))) - elif state == 'absent': - for port in item.get('name'): - if 'all' in port: - module.fail_json(msg='cannot enable on all the ports') - else: - commands.append('no lldp enable ports {0}'.format(str(port))) - - -def main(): - """ main entry point for module execution - """ - interfaces_spec = dict( - name=dict(type='list'), - state=dict(choices=['present', 'absent', - 'enabled', 'disabled']) - ) - - argument_spec = dict( - interfaces=dict(type='list', elements='dict', options=interfaces_spec), - state=dict(choices=['present', 'absent', - 'enabled', 'disabled']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - warnings = list() - - result = {'changed': False} - - if warnings: - result['warnings'] = warnings - - if module.params['check_running_config'] is False: - HAS_LLDP = None - else: - HAS_LLDP = has_lldp(module) - - commands = [] - state = module.params['state'] - - if state is None: - if HAS_LLDP: - map_obj_to_commands(module, commands) - else: - module.fail_json(msg='LLDP is not running') - else: - if state == 'absent' and HAS_LLDP is None: - commands.append('no lldp run') - - if state == 'absent' and HAS_LLDP: - commands.append('no lldp run') - - elif state == 'present': - if not HAS_LLDP: - commands.append('lldp run') - if module.params.get('interfaces'): - map_obj_to_commands(module, commands) - - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/network/icx/icx_logging.py b/lib/ansible/modules/network/icx/icx_logging.py deleted file mode 100644 index 7566445ae0..0000000000 --- a/lib/ansible/modules/network/icx/icx_logging.py +++ /dev/null @@ -1,582 +0,0 @@ -#!/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': 'community'} - - -DOCUMENTATION = """ ---- -module: icx_logging -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Manage logging on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of logging - on Ruckus ICX 7000 series switches. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - dest: - description: - - Destination of the logs. - choices: ['on', 'host', 'console', 'buffered', 'persistence', 'rfc5424'] - type: str - name: - description: - - ipv4 address/ipv6 address/name of syslog server. - type: str - udp_port: - description: - - UDP port of destination host(syslog server). - type: str - facility: - description: - - Specifies log facility to log messages from the device. - choices: ['auth','cron','daemon','kern','local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7', 'user', - 'lpr','mail','news','syslog','sys9','sys10','sys11','sys12','sys13','sys14','user','uucp'] - type: str - level: - description: - - Specifies the message level. - type: list - choices: ['alerts', 'critical', 'debugging', 'emergencies', 'errors', 'informational', - 'notifications', 'warnings'] - aggregate: - description: - - List of logging definitions. - type: list - suboptions: - dest: - description: - - Destination of the logs. - choices: ['on', 'host', 'console', 'buffered', 'persistence', 'rfc5424'] - type: str - name: - description: - - ipv4 address/ipv6 address/name of syslog server. - type: str - udp_port: - description: - - UDP port of destination host(syslog server). - type: str - facility: - description: - - Specifies log facility to log messages from the device. - choices: ['auth','cron','daemon','kern','local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7', 'user', - 'lpr','mail','news','syslog','sys9','sys10','sys11','sys12','sys13','sys14','user','uucp'] - type: str - level: - description: - - Specifies the message level. - type: list - choices: ['alerts', 'critical', 'debugging', 'emergencies', 'errors', 'informational', - 'notifications', 'warnings'] - state: - description: - - State of the logging configuration. - choices: ['present', 'absent'] - type: str - 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 overridden, by specifying it as module parameter. - type: bool - state: - description: - - State of the logging configuration. - default: present - choices: ['present', 'absent'] - type: str - 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 overridden, by specifying it as module parameter. - type: bool - default: yes -""" - -EXAMPLES = """ -- name: Configure host logging. - icx_logging: - dest: host - name: 172.16.0.1 - udp_port: 5555 -- name: Remove host logging configuration. - icx_logging: - dest: host - name: 172.16.0.1 - udp_port: 5555 - state: absent -- name: Disables the real-time display of syslog messages. - icx_logging: - dest: console - state: absent -- name: Enables local syslog logging. - icx_logging: - dest : on - state: present -- name: configure buffer level. - icx_logging: - dest: buffered - level: critical -- name: Configure logging using aggregate - icx_logging: - aggregate: - - { dest: buffered, level: ['notifications','errors'] } -- name: remove logging using aggregate - icx_logging: - aggregate: - - { dest: console } - - { dest: host, name: 172.16.0.1, udp_port: 5555 } - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - logging host 172.16.0.1 - - logging console -""" - -import re -from copy import deepcopy -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection, ConnectionError, exec_command -from ansible.module_utils.network.common.utils import remove_default_spec, validate_ip_v6_address -from ansible.module_utils.network.icx.icx import get_config, load_config - - -def search_obj_in_list(name, lst): - for o in lst: - if o['name'] == name: - return o - - -def diff_in_list(want, have): - adds = set() - removes = set() - for w in want: - if w['dest'] == 'buffered': - for h in have: - if h['dest'] == 'buffered': - adds = w['level'] - h['level'] - removes = h['level'] - w['level'] - return adds, removes - return adds, removes - - -def map_obj_to_commands(updates): - dest_group = ('host', 'console', 'persistence', 'enable') - commands = list() - want, have = updates - - for w in want: - dest = w['dest'] - name = w['name'] - level = w['level'] - state = w['state'] - udp_port = w['udp_port'] - facility = w['facility'] - del w['state'] - del w['facility'] - - facility_name = '' - facility_level = '' - if name is not None and validate_ip_v6_address(name): - name = 'ipv6 ' + name - - if facility: - for item in have: - if item['dest'] == 'facility': - facility_name = item['dest'] - facility_level = item['facility'] - - if state == 'absent': - if have == []: - if facility: - commands.append('no logging facility') - - if dest == 'buffered': - for item in have: - if item['dest'] == 'buffered': - want_level = level - have_level = item['level'] - for item in want_level: - commands.append('no logging buffered {0}'.format(item)) - - if dest == 'host': - if name and udp_port: - commands.append('no logging host {0} udp-port {1}'.format(name, udp_port)) - elif name: - commands.append('no logging host {0}'.format(name)) - else: - if dest == 'rfc5424': - commands.append('no logging enable {0}'.format(dest)) - else: - if dest != 'buffered': - commands.append('no logging {0}'.format(dest)) - - if facility: - if facility_name == 'facility' and facility_level != 'user': - commands.append('no logging facility') - - if dest == 'buffered': - for item in have: - if item['dest'] == 'buffered': - want_level = level - have_level = item['level'] - for item in want_level: - if item in have_level: - commands.append('no logging buffered {0}'.format(item)) - - if w in have: - if dest == 'host': - if name and udp_port: - commands.append('no logging host {0} udp-port {1}'.format(name, udp_port)) - elif name: - commands.append('no logging host {0}'.format(name)) - else: - if dest == 'rfc5424': - commands.append('no logging enable {0}'.format(dest)) - else: - if dest != 'buffered': - commands.append('no logging {0}'.format(dest)) - - if state == 'present': - if facility: - if facility != facility_level: - commands.append('logging facility {0}'.format(facility)) - if w not in have: - if dest == 'host': - if name and udp_port: - commands.append('logging host {0} udp-port {1}'.format(name, udp_port)) - elif name: - commands.append('logging host {0}'.format(name)) - elif dest == 'buffered': - adds, removes = diff_in_list(want, have) - for item in adds: - commands.append('logging buffered {0}'.format(item)) - for item in removes: - commands.append('no logging buffered {0}'.format(item)) - elif dest == 'rfc5424': - commands.append('logging enable {0}'.format(dest)) - else: - commands.append('logging {0}'.format(dest)) - - return commands - - -def parse_port(line, dest): - port = None - if dest == 'host': - match = re.search(r'logging host \S+\s+udp-port\s+(\d+)', line, re.M) - if match: - port = match.group(1) - else: - match_port = re.search(r'logging host ipv6 \S+\s+udp-port\s+(\d+)', line, re.M) - if match_port: - port = match_port.group(1) - return port - - -def parse_name(line, dest): - name = None - if dest == 'host': - match = re.search(r'logging host (\S+)', line, re.M) - if match: - if match.group(1) == 'ipv6': - ipv6_add = re.search(r'logging host ipv6 (\S+)', line, re.M) - name = ipv6_add.group(1) - else: - name = match.group(1) - - return name - - -def parse_address(line, dest): - if dest == 'host': - match = re.search(r'^logging host ipv6 (\S+)', line.strip(), re.M) - if match: - return True - return False - - -def map_config_to_obj(module): - obj = [] - facility = '' - addr6 = False - dest_group = ('host', 'console', 'buffered', 'persistence', 'enable') - dest_level = ('alerts', 'critical', 'debugging', 'emergencies', 'errors', 'informational', 'notifications', 'warnings') - buff_level = list() - if module.params['check_running_config'] is False: - return [] - data = get_config(module, flags=['| include logging']) - facility_match = re.search(r'^logging facility (\S+)', data, re.M) - if facility_match: - facility = facility_match.group(1) - obj.append({ - 'dest': 'facility', - 'facility': facility - }) - else: - obj.append({ - 'dest': 'facility', - 'facility': 'user' - }) - for line in data.split('\n'): - match = re.search(r'^logging (\S+)', line.strip(), re.M) - if match: - - if match.group(1) in dest_group: - dest = match.group(1) - if dest == 'host': - obj.append({ - 'dest': dest, - 'name': parse_name(line.strip(), dest), - 'udp_port': parse_port(line, dest), - 'level': None, - 'addr6': parse_address(line, dest) - - }) - elif dest == 'buffered': - obj.append({ - 'dest': dest, - 'level': None, - 'name': None, - 'udp_port': None, - 'addr6': False - }) - else: - if dest == 'enable': - dest = 'rfc5424' - obj.append({ - 'dest': dest, - 'level': None, - 'name': None, - 'udp_port': None, - 'addr6': False - }) - else: - - ip_match = re.search(r'^no logging buffered (\S+)', line, re.M) - if ip_match: - dest = 'buffered' - buff_level.append(ip_match.group(1)) - if 'no logging on' not in data: - obj.append({ - 'dest': 'on', - 'level': None, - 'name': None, - 'udp_port': None, - 'addr6': False - - }) - levels = set() - for items in dest_level: - if items not in buff_level: - levels.add(items) - obj.append({ - 'dest': 'buffered', - 'level': levels, - 'name': None, - 'udp_port': None, - 'addr6': False - - }) - return obj - - -def count_terms(check, param=None): - count = 0 - for term in check: - if param[term] is not None: - count += 1 - return count - - -def check_required_if(module, spec, param): - for sp in spec: - missing = [] - max_missing_count = 0 - is_one_of = False - if len(sp) == 4: - key, val, requirements, is_one_of = sp - else: - key, val, requirements = sp - - if is_one_of: - max_missing_count = len(requirements) - term = 'any' - else: - term = 'all' - - if key in param and param[key] == val: - for check in requirements: - count = count_terms((check,), param) - if count == 0: - missing.append(check) - if len(missing) and len(missing) >= max_missing_count: - msg = "%s is %s but %s of the following are missing: %s" % (key, val, term, ', '.join(missing)) - module.fail_json(msg=msg) - - -def map_params_to_obj(module, required_if=None): - obj = [] - addr6 = False - aggregate = module.params.get('aggregate') - - if aggregate: - for item in aggregate: - if item['name'] is not None and validate_ip_v6_address(item['name']): - addr6 = True - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - check_required_if(module, required_if, item) - item.update({'addr6': addr6}) - - d = item.copy() - d['level'] = set(d['level']) if d['level'] is not None else None - if d['dest'] != 'host': - d['name'] = None - d['udp_port'] = None - - if d['dest'] != 'buffered': - d['level'] = None - del d['check_running_config'] - obj.append(d) - - else: - if module.params['name'] is not None and validate_ip_v6_address(module.params['name']): - addr6 = True - if module.params['dest'] != 'host': - module.params['name'] = None - module.params['udp_port'] = None - - if module.params['dest'] != 'buffered': - module.params['level'] = None - - obj.append({ - 'dest': module.params['dest'], - 'name': module.params['name'], - 'udp_port': module.params['udp_port'], - 'level': set(module.params['level']) if module.params['level'] else None, - 'facility': module.params['facility'], - 'state': module.params['state'], - 'addr6': addr6 - }) - return obj - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - dest=dict( - type='str', - choices=[ - 'on', - 'host', - 'console', - 'buffered', - 'persistence', - 'rfc5424']), - name=dict( - type='str'), - udp_port=dict(), - level=dict( - type='list', - choices=[ - 'alerts', - 'critical', - 'debugging', - 'emergencies', - 'errors', - 'informational', - 'notifications', - 'warnings']), - facility=dict( - type='str', - choices=[ - 'auth', - 'cron', - 'daemon', - 'kern', - 'local0', - 'local1', - 'local2', - 'local3', - 'local4', - 'local5', - 'local6', - 'local7', - 'user', - 'lpr', - 'mail', - 'news', - 'syslog', - 'sys9', - 'sys10', - 'sys11', - 'sys12', - 'sys13', - 'sys14', - 'user', - 'uucp']), - state=dict( - default='present', - choices=[ - 'present', - 'absent']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG']))) - - aggregate_spec = deepcopy(element_spec) - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - ) - - argument_spec.update(element_spec) - required_if = [('dest', 'host', ['name']), - ('dest', 'buffered', ['level'])] - module = AnsibleModule(argument_spec=argument_spec, - required_if=required_if, - supports_check_mode=True) - - result = {'changed': False} - warnings = list() - - exec_command(module, 'skip') - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module, required_if=required_if) - have = map_config_to_obj(module) - result['want'] = want - result['have'] = have - - commands = map_obj_to_commands((want, have)) - result['commands'] = commands - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/network/icx/icx_ping.py b/lib/ansible/modules/network/icx/icx_ping.py deleted file mode 100644 index 0c9c262aeb..0000000000 --- a/lib/ansible/modules/network/icx/icx_ping.py +++ /dev/null @@ -1,270 +0,0 @@ -#!/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': 'community'} - - -DOCUMENTATION = """ ---- -module: icx_ping -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Tests reachability using ping from Ruckus ICX 7000 series switches -description: - - Tests reachability using ping from switch to a remote destination. -notes: - - Tested against ICX 10.1 -options: - count: - description: - - Number of packets to send. Default is 1. - type: int - dest: - description: - - ip-addr | host-name | vrf vrf-name | ipv6 [ ipv6-addr | host-name | vrf vrf-name] (resolvable by switch) of the remote node. - required: true - type: str - timeout: - description: - - Specifies the time, in milliseconds for which the device waits for a reply from the pinged device. - The value can range from 1 to 4294967296. The default is 5000 (5 seconds). - type: int - ttl: - description: - - Specifies the time to live as a maximum number of hops. The value can range from 1 to 255. The default is 64. - type: int - size: - description: - - Specifies the size of the ICMP data portion of the packet, in bytes. This is the payload and does not include the header. - The value can range from 0 to 10000. The default is 16.. - type: int - source: - description: - - IP address to be used as the origin of the ping packets. - type: str - vrf: - description: - - Specifies the Virtual Routing and Forwarding (VRF) instance of the device to be pinged. - type: str - state: - description: - - Determines if the expected result is success or fail. - type: str - choices: [ absent, present ] - default: present -""" - -EXAMPLES = r''' -- name: Test reachability to 10.10.10.10 - icx_ping: - dest: 10.10.10.10 - -- name: Test reachability to ipv6 address from source with timeout - icx_ping: - dest: ipv6 2001:cdba:0000:0000:0000:0000:3257:9652 - source: 10.1.1.1 - timeout: 100000 - -- name: Test reachability to 10.1.1.1 through vrf using 5 packets - icx_ping: - dest: 10.1.1.1 - vrf: x.x.x.x - count: 5 - -- name: Test unreachability to 10.30.30.30 - icx_ping: - dest: 10.40.40.40 - state: absent - -- name: Test reachability to ipv4 with ttl and packet size - icx_ping: - dest: 10.10.10.10 - ttl: 20 - size: 500 -''' - -RETURN = ''' -commands: - description: Show the command sent. - returned: always - type: list - sample: ["ping 10.40.40.40 count 20 source loopback0", "ping 10.40.40.40"] -packet_loss: - description: Percentage of packets lost. - returned: always - type: str - sample: "0%" -packets_rx: - description: Packets successfully received. - returned: always - type: int - sample: 20 -packets_tx: - description: Packets successfully transmitted. - returned: always - type: int - sample: 20 -rtt: - description: Show RTT stats. - returned: always - type: dict - sample: {"avg": 2, "max": 8, "min": 1} -''' - -from ansible.module_utils._text import to_text -from ansible.module_utils.network.icx.icx import run_commands -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection, ConnectionError -import re - - -def build_ping(dest, count=None, source=None, timeout=None, ttl=None, size=None, vrf=None): - """ - Function to build the command to send to the terminal for the switch - to execute. All args come from the module's unique params. - """ - - if vrf is not None: - cmd = "ping vrf {0} {1}".format(vrf, dest) - else: - cmd = "ping {0}".format(dest) - - if count is not None: - cmd += " count {0}".format(str(count)) - - if timeout is not None: - cmd += " timeout {0}".format(str(timeout)) - - if ttl is not None: - cmd += " ttl {0}".format(str(ttl)) - - if size is not None: - cmd += " size {0}".format(str(size)) - - if source is not None: - cmd += " source {0}".format(source) - - return cmd - - -def parse_ping(ping_stats): - """ - Function used to parse the statistical information from the ping response. - Example: "Success rate is 100 percent (5/5), round-trip min/avg/max=40/51/55 ms." - Returns the percent of packet loss, received packets, transmitted packets, and RTT dict. - """ - if ping_stats.startswith('Success'): - rate_re = re.compile(r"^\w+\s+\w+\s+\w+\s+(?P<pct>\d+)\s+\w+\s+\((?P<rx>\d+)/(?P<tx>\d+)\)") - rtt_re = re.compile(r".*,\s+\S+\s+\S+=(?P<min>\d+)/(?P<avg>\d+)/(?P<max>\d+)\s+\w+\.+\s*$|.*\s*$") - - rate = rate_re.match(ping_stats) - rtt = rtt_re.match(ping_stats) - return rate.group("pct"), rate.group("rx"), rate.group("tx"), rtt.groupdict() - else: - rate_re = re.compile(r"^Sending+\s+(?P<tx>\d+),") - rate = rate_re.match(ping_stats) - rtt = {'avg': 0, 'max': 0, 'min': 0} - return 0, 0, rate.group('tx'), rtt - - -def validate_results(module, loss, results): - """ - This function is used to validate whether the ping results were unexpected per "state" param. - """ - state = module.params["state"] - if state == "present" and loss == 100: - module.fail_json(msg="Ping failed unexpectedly", **results) - elif state == "absent" and loss < 100: - module.fail_json(msg="Ping succeeded unexpectedly", **results) - - -def validate_fail(module, responses): - if ("Success" in responses or "No reply" in responses) is False: - module.fail_json(msg=responses) - - -def validate_parameters(module, timeout, count): - if timeout and not 1 <= int(timeout) <= 4294967294: - module.fail_json(msg="bad value for timeout - valid range (1-4294967294)") - if count and not 1 <= int(count) <= 4294967294: - module.fail_json(msg="bad value for count - valid range (1-4294967294)") - - -def main(): - """ main entry point for module execution - """ - argument_spec = dict( - count=dict(type="int"), - dest=dict(type="str", required=True), - timeout=dict(type="int"), - ttl=dict(type="int"), - size=dict(type="int"), - source=dict(type="str"), - state=dict(type="str", choices=["absent", "present"], default="present"), - vrf=dict(type="str") - ) - - module = AnsibleModule(argument_spec=argument_spec) - - count = module.params["count"] - dest = module.params["dest"] - source = module.params["source"] - timeout = module.params["timeout"] - ttl = module.params["ttl"] - size = module.params["size"] - vrf = module.params["vrf"] - results = {} - warnings = list() - - if warnings: - results["warnings"] = warnings - - response = '' - try: - validate_parameters(module, timeout, count) - results["commands"] = [build_ping(dest, count, source, timeout, ttl, size, vrf)] - ping_results = run_commands(module, commands=results["commands"]) - ping_results_list = ping_results[0].split("\n") - - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - validate_fail(module, ping_results[0]) - - stats = "" - statserror = '' - for line in ping_results_list: - if line.startswith('Sending'): - statserror = line - if line.startswith('Success'): - stats = line - elif line.startswith('No reply'): - stats = statserror - - success, rx, tx, rtt = parse_ping(stats) - loss = abs(100 - int(success)) - results["packet_loss"] = str(loss) + "%" - results["packets_rx"] = int(rx) - results["packets_tx"] = int(tx) - - # Convert rtt values to int - for k, v in rtt.items(): - if rtt[k] is not None: - rtt[k] = int(v) - - results["rtt"] = rtt - - validate_results(module, loss, results) - - module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/network/icx/icx_static_route.py b/lib/ansible/modules/network/icx/icx_static_route.py deleted file mode 100644 index bffa2130aa..0000000000 --- a/lib/ansible/modules/network/icx/icx_static_route.py +++ /dev/null @@ -1,315 +0,0 @@ -#!/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': 'community'} - -DOCUMENTATION = """ ---- -module: icx_static_route -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Manage static IP routes on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of static - IP routes on Ruckus ICX network devices. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - prefix: - description: - - Network prefix of the static route. - type: str - mask: - description: - - Network prefix mask of the static route. - type: str - next_hop: - description: - - Next hop IP of the static route. - type: str - admin_distance: - description: - - Admin distance of the static route. Range is 1 to 255. - type: int - aggregate: - description: List of static route definitions. - type: list - suboptions: - prefix: - description: - - Network prefix of the static route. - type: str - mask: - description: - - Network prefix mask of the static route. - type: str - next_hop: - description: - - Next hop IP of the static route. - type: str - admin_distance: - description: - - Admin distance of the static route. Range is 1 to 255. - type: int - state: - description: - - State of the static route configuration. - type: str - choices: ['present', 'absent'] - 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 overridden, by specifying it as module parameter. - type: bool - purge: - description: - - Purge routes not defined in the I(aggregate) parameter. - default: no - type: bool - state: - description: - - State of the static route configuration. - type: str - default: present - choices: ['present', 'absent'] - 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 overridden, by specifying it as module parameter. - type: bool - default: yes -""" - -EXAMPLES = """ -- name: configure static route - icx_static_route: - prefix: 192.168.2.0/24 - next_hop: 10.0.0.1 - -- name: remove configuration - icx_static_route: - prefix: 192.168.2.0 - mask: 255.255.255.0 - next_hop: 10.0.0.1 - state: absent - -- name: Add static route aggregates - icx_static_route: - aggregate: - - { prefix: 172.16.32.0, mask: 255.255.255.0, next_hop: 10.0.0.8 } - - { prefix: 172.16.33.0, mask: 255.255.255.0, next_hop: 10.0.0.8 } - -- name: remove static route aggregates - icx_static_route: - aggregate: - - { prefix: 172.16.32.0, mask: 255.255.255.0, next_hop: 10.0.0.8 } - - { prefix: 172.16.33.0, mask: 255.255.255.0, next_hop: 10.0.0.8 } - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - ip route 192.168.2.0 255.255.255.0 10.0.0.1 -""" - - -from copy import deepcopy -import re - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import ConnectionError -from ansible.module_utils.network.common.utils import remove_default_spec -from ansible.module_utils.network.icx.icx import get_config, load_config - -try: - from ipaddress import ip_network - HAS_IPADDRESS = True -except ImportError: - HAS_IPADDRESS = False - - -def map_obj_to_commands(want, have, module): - commands = list() - purge = module.params['purge'] - for w in want: - for h in have: - for key in ['prefix', 'mask', 'next_hop']: - if w[key] != h[key]: - break - else: - break - else: - h = None - - prefix = w['prefix'] - mask = w['mask'] - next_hop = w['next_hop'] - admin_distance = w.get('admin_distance') - if not admin_distance and h: - w['admin_distance'] = admin_distance = h['admin_distance'] - state = w['state'] - del w['state'] - - if state == 'absent' and have == []: - commands.append('no ip route %s %s %s' % (prefix, mask, next_hop)) - - if state == 'absent' and w in have: - commands.append('no ip route %s %s %s' % (prefix, mask, next_hop)) - elif state == 'present' and w not in have: - if admin_distance: - commands.append('ip route %s %s %s distance %s' % (prefix, mask, next_hop, admin_distance)) - else: - commands.append('ip route %s %s %s' % (prefix, mask, next_hop)) - if purge: - commands = [] - for h in have: - if h not in want: - commands.append('no ip route %s %s %s' % (prefix, mask, next_hop)) - return commands - - -def map_config_to_obj(module): - obj = [] - compare = module.params['check_running_config'] - out = get_config(module, flags='| include ip route', compare=compare) - - for line in out.splitlines(): - splitted_line = line.split() - if len(splitted_line) not in (4, 5, 6): - continue - cidr = ip_network(to_text(splitted_line[2])) - prefix = str(cidr.network_address) - mask = str(cidr.netmask) - next_hop = splitted_line[3] - if len(splitted_line) == 6: - admin_distance = splitted_line[5] - else: - admin_distance = '1' - - obj.append({ - 'prefix': prefix, 'mask': mask, 'next_hop': next_hop, - 'admin_distance': admin_distance - }) - - return obj - - -def prefix_length_parser(prefix, mask, module): - if '/' in prefix and mask is not None: - module.fail_json(msg='Ambigous, specifed both length and mask') - if '/' in prefix: - cidr = ip_network(to_text(prefix)) - prefix = str(cidr.network_address) - mask = str(cidr.netmask) - return prefix, mask - - -def map_params_to_obj(module, required_together=None): - keys = ['prefix', 'mask', 'next_hop', 'admin_distance', 'state'] - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - route = item.copy() - for key in keys: - if route.get(key) is None: - route[key] = module.params.get(key) - - module._check_required_together(required_together, route) - - prefix, mask = prefix_length_parser(route['prefix'], route['mask'], module) - route.update({'prefix': prefix, 'mask': mask}) - - obj.append(route) - else: - module._check_required_together(required_together, module.params) - prefix, mask = prefix_length_parser(module.params['prefix'], module.params['mask'], module) - - obj.append({ - 'prefix': prefix, - 'mask': mask, - 'next_hop': module.params['next_hop'].strip(), - 'admin_distance': module.params.get('admin_distance'), - 'state': module.params['state'], - }) - - for route in obj: - if route['admin_distance']: - route['admin_distance'] = str(route['admin_distance']) - - return obj - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - prefix=dict(type='str'), - mask=dict(type='str'), - next_hop=dict(type='str'), - admin_distance=dict(type='int'), - state=dict(default='present', choices=['present', 'absent']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['prefix'] = dict(required=True) - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - purge=dict(default=False, type='bool') - ) - - argument_spec.update(element_spec) - - required_one_of = [['aggregate', 'prefix']] - required_together = [['prefix', 'next_hop']] - mutually_exclusive = [['aggregate', 'prefix']] - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - if not HAS_IPADDRESS: - module.fail_json(msg="ipaddress python package is required") - - warnings = list() - - result = {'changed': False} - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module, required_together=required_together) - have = map_config_to_obj(module) - - commands = map_obj_to_commands(want, have, module) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/network/icx/icx_system.py b/lib/ansible/modules/network/icx/icx_system.py deleted file mode 100644 index 449601fd7b..0000000000 --- a/lib/ansible/modules/network/icx/icx_system.py +++ /dev/null @@ -1,471 +0,0 @@ -#!/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': 'community'} - -DOCUMENTATION = """ ---- -module: icx_system -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Manage the system attributes on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of node system attributes - on Ruckus ICX 7000 series switches. It provides an option to configure host system - parameters or remove those parameters from the device active - configuration. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - hostname: - description: - - Configure the device hostname parameter. This option takes an ASCII string value. - type: str - domain_name: - description: - - Configure the IP domain name on the remote device to the provided value. - Value should be in the dotted name form and - will be appended to the hostname to create a fully-qualified domain name. - type: list - domain_search: - description: - - Provides the list of domain names to - append to the hostname for the purpose of doing name resolution. - This argument accepts a list of names and will be reconciled - with the current active configuration on the running node. - type: list - name_servers: - description: - - List of DNS name servers by IP address to use to perform name resolution - lookups. - type: list - aaa_servers: - description: - - Configures radius/tacacs server - type: list - suboptions: - type: - description: - - specify the type of the server - type: str - choices: ['radius','tacacs'] - hostname: - description: - - Configures the host name of the RADIUS server - type: str - auth_port_type: - description: - - specifies the type of the authentication port - type: str - choices: ['auth-port'] - auth_port_num: - description: - - Configures the authentication UDP port. The default value is 1812. - type: str - acct_port_num: - description: - - Configures the accounting UDP port. The default value is 1813. - type: str - acct_type: - description: - - Usage of the accounting port. - type: str - choices: ['accounting-only', 'authentication-only','authorization-only', default] - auth_key: - description: - - Configure the key for the server - type: str - auth_key_type: - description: - - List of authentication level specified in the choices - type: list - choices: ['dot1x','mac-auth','web-auth'] - state: - description: - - State of the configuration - values in the device's current active configuration. When set - to I(present), the values should be configured in the device active - configuration and when set to I(absent) the values should not be - in the device active configuration - type: str - default: present - choices: ['present', 'absent'] - 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 overridden, by specifying it as module parameter. - type: bool - default: yes -""" - -EXAMPLES = """ -- name: configure hostname and domain name - icx_system: - hostname: icx - domain_search: - - ansible.com - - redhat.com - - ruckus.com - -- name: configure radius server of type auth-port - icx_system: - aaa_servers: - - type: radius - hostname: radius-server - auth_port_type: auth-port - auth_port_num: 1821 - acct_port_num: 1321 - acct_type: accounting-only - auth_key: abc - auth_key_type: - - dot1x - - mac-auth - -- name: configure tacacs server - icx_system: - aaa_servers: - - type: tacacs - hostname: tacacs-server - auth_port_type: auth-port - auth_port_num: 1821 - acct_port_num: 1321 - acct_type: accounting-only - auth_key: xyz - -- name: configure name servers - icx_system: - name_servers: - - 8.8.8.8 - - 8.8.4.4 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - hostname icx - - ip domain name test.example.com - - radius-server host 172.16.10.12 auth-port 2083 acct-port 1850 default key abc dot1x mac-auth - - tacacs-server host 10.2.3.4 auth-port 4058 authorization-only key xyz - -""" - - -import re -from copy import deepcopy -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.network.icx.icx import get_config, load_config -from ansible.module_utils.network.common.utils import ComplexList, validate_ip_v6_address -from ansible.module_utils.connection import Connection, ConnectionError, exec_command - - -def diff_list(want, have): - adds = [w for w in want if w not in have] - removes = [h for h in have if h not in want] - return (adds, removes) - - -def map_obj_to_commands(want, have, module): - commands = list() - state = module.params['state'] - - def needs_update(x): - return want.get(x) is not None and (want.get(x) != have.get(x)) - - if state == 'absent': - if have['name_servers'] == [] and have['aaa_servers'] == [] and have['domain_search'] == [] and have['hostname'] is None: - if want['hostname']: - commands.append('no hostname') - - if want['domain_search']: - for item in want['domain_search']: - commands.append('no ip dns domain-list %s' % item) - - if want['name_servers']: - for item in want['name_servers']: - commands.append('no ip dns server-address %s' % item) - - if want['aaa_servers']: - want_servers = [] - want_server = want['aaa_servers'] - if want_server: - want_list = deepcopy(want_server) - for items in want_list: - items['auth_key'] = None - want_servers.append(items) - for item in want_servers: - ipv6addr = validate_ip_v6_address(item['hostname']) - if ipv6addr: - commands.append('no ' + item['type'] + '-server host ipv6 ' + item['hostname']) - else: - commands.append('no ' + item['type'] + '-server host ' + item['hostname']) - - if want['hostname']: - if have['hostname'] == want['hostname']: - commands.append('no hostname') - - if want['domain_search']: - for item in want['domain_search']: - if item in have['domain_search']: - commands.append('no ip dns domain-list %s' % item) - - if want['name_servers']: - for item in want['name_servers']: - if item in have['name_servers']: - commands.append('no ip dns server-address %s' % item) - - if want['aaa_servers']: - want_servers = [] - want_server = want['aaa_servers'] - have_server = have['aaa_servers'] - if want_server: - want_list = deepcopy(want_server) - for items in want_list: - items['auth_key'] = None - want_servers.append(items) - for item in want_servers: - if item in have_server: - ipv6addr = validate_ip_v6_address(item['hostname']) - if ipv6addr: - commands.append('no ' + item['type'] + '-server host ipv6 ' + item['hostname']) - else: - commands.append('no ' + item['type'] + '-server host ' + item['hostname']) - - elif state == 'present': - if needs_update('hostname'): - commands.append('hostname %s' % want['hostname']) - - if want['domain_search']: - adds, removes = diff_list(want['domain_search'], have['domain_search']) - for item in removes: - commands.append('no ip dns domain-list %s' % item) - for item in adds: - commands.append('ip dns domain-list %s' % item) - - if want['name_servers']: - adds, removes = diff_list(want['name_servers'], have['name_servers']) - for item in removes: - commands.append('no ip dns server-address %s' % item) - for item in adds: - commands.append('ip dns server-address %s' % item) - - if want['aaa_servers']: - want_servers = [] - want_server = want['aaa_servers'] - have_server = have['aaa_servers'] - want_list = deepcopy(want_server) - for items in want_list: - items['auth_key'] = None - want_servers.append(items) - - adds, removes = diff_list(want_servers, have_server) - - for item in removes: - ip6addr = validate_ip_v6_address(item['hostname']) - if ip6addr: - cmd = 'no ' + item['type'] + '-server host ipv6 ' + item['hostname'] - else: - cmd = 'no ' + item['type'] + '-server host ' + item['hostname'] - commands.append(cmd) - - for w_item in adds: - for item in want_server: - if item['hostname'] == w_item['hostname'] and item['type'] == w_item['type']: - auth_key = item['auth_key'] - - ip6addr = validate_ip_v6_address(w_item['hostname']) - if ip6addr: - cmd = w_item['type'] + '-server host ipv6 ' + w_item['hostname'] - else: - cmd = w_item['type'] + '-server host ' + w_item['hostname'] - if w_item['auth_port_type']: - cmd += ' ' + w_item['auth_port_type'] + ' ' + w_item['auth_port_num'] - if w_item['acct_port_num'] and w_item['type'] == 'radius': - cmd += ' acct-port ' + w_item['acct_port_num'] - if w_item['type'] == 'tacacs': - if any((w_item['acct_port_num'], w_item['auth_key_type'])): - module.fail_json(msg='acct_port and auth_key_type is not applicable for tacacs server') - if w_item['acct_type']: - cmd += ' ' + w_item['acct_type'] - if auth_key is not None: - cmd += ' key ' + auth_key - if w_item['auth_key_type'] and w_item['type'] == 'radius': - val = '' - for y in w_item['auth_key_type']: - val = val + ' ' + y - cmd += val - commands.append(cmd) - - return commands - - -def parse_hostname(config): - match = re.search(r'^hostname (\S+)', config, re.M) - if match: - return match.group(1) - - -def parse_domain_search(config): - match = re.findall(r'^ip dns domain[- ]list (\S+)', config, re.M) - matches = list() - for name in match: - matches.append(name) - return matches - - -def parse_name_servers(config): - matches = list() - values = list() - lines = config.split('\n') - for line in lines: - if 'ip dns server-address' in line: - values = line.split(' ') - for val in values: - match = re.search(r'([0-9.]+)', val) - if match: - matches.append(match.group()) - - return matches - - -def parse_aaa_servers(config): - configlines = config.split('\n') - obj = [] - for line in configlines: - auth_key_type = [] - if 'radius-server' in line or 'tacacs-server' in line: - aaa_type = 'radius' if 'radius-server' in line else 'tacacs' - match = re.search(r'(host ipv6 (\S+))|(host (\S+))', line) - if match: - hostname = match.group(2) if match.group(2) is not None else match.group(4) - match = re.search(r'auth-port ([0-9]+)', line) - if match: - auth_port_num = match.group(1) - else: - auth_port_num = None - match = re.search(r'acct-port ([0-9]+)', line) - if match: - acct_port_num = match.group(1) - else: - acct_port_num = None - match = re.search(r'acct-port [0-9]+ (\S+)', line) - if match: - acct_type = match.group(1) - else: - acct_type = None - if aaa_type == 'tacacs': - match = re.search(r'auth-port [0-9]+ (\S+)', line) - if match: - acct_type = match.group(1) - else: - acct_type = None - match = re.search(r'(dot1x)', line) - if match: - auth_key_type.append('dot1x') - match = re.search(r'(mac-auth)', line) - if match: - auth_key_type.append('mac-auth') - match = re.search(r'(web-auth)', line) - if match: - auth_key_type.append('web-auth') - - obj.append({ - 'type': aaa_type, - 'hostname': hostname, - 'auth_port_type': 'auth-port', - 'auth_port_num': auth_port_num, - 'acct_port_num': acct_port_num, - 'acct_type': acct_type, - 'auth_key': None, - 'auth_key_type': set(auth_key_type) if len(auth_key_type) > 0 else None - }) - - return obj - - -def map_config_to_obj(module): - compare = module.params['check_running_config'] - config = get_config(module, None, compare=compare) - return { - 'hostname': parse_hostname(config), - 'domain_search': parse_domain_search(config), - 'name_servers': parse_name_servers(config), - 'aaa_servers': parse_aaa_servers(config) - } - - -def map_params_to_obj(module): - if module.params['aaa_servers']: - for item in module.params['aaa_servers']: - if item['auth_key_type']: - item['auth_key_type'] = set(item['auth_key_type']) - obj = { - 'hostname': module.params['hostname'], - 'domain_name': module.params['domain_name'], - 'domain_search': module.params['domain_search'], - 'name_servers': module.params['name_servers'], - 'state': module.params['state'], - 'aaa_servers': module.params['aaa_servers'] - } - return obj - - -def main(): - """ Main entry point for Ansible module execution - """ - server_spec = dict( - type=dict(choices=['radius', 'tacacs']), - hostname=dict(), - auth_port_type=dict(choices=['auth-port']), - auth_port_num=dict(), - acct_port_num=dict(), - acct_type=dict(choices=['accounting-only', 'authentication-only', 'authorization-only', 'default']), - auth_key=dict(), - auth_key_type=dict(type='list', choices=['dot1x', 'mac-auth', 'web-auth']) - ) - argument_spec = dict( - hostname=dict(), - - domain_name=dict(type='list'), - domain_search=dict(type='list'), - name_servers=dict(type='list'), - - aaa_servers=dict(type='list', elements='dict', options=server_spec), - state=dict(choices=['present', 'absent'], default='present'), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - - result['warnings'] = warnings - exec_command(module, 'skip') - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands(want, have, module) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/modules/network/icx/icx_user.py b/lib/ansible/modules/network/icx/icx_user.py deleted file mode 100644 index 3a92cf79cd..0000000000 --- a/lib/ansible/modules/network/icx/icx_user.py +++ /dev/null @@ -1,391 +0,0 @@ -#!/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': 'community'} - -DOCUMENTATION = """ ---- -module: icx_user -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Manage the user accounts on Ruckus ICX 7000 series switches. -description: - - This module creates or updates user account on network devices. It allows playbooks to manage - either individual usernames or the aggregate of usernames in the - current running config. It also supports purging usernames from the - configuration that are not explicitly defined. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - aggregate: - description: - - The set of username objects to be configured on the remote - ICX device. The list entries can either be the username - or a hash of username and properties. This argument is mutually - exclusive with the C(name) argument. - aliases: ['users', 'collection'] - type: list - suboptions: - name: - description: - - The username to be configured on the ICX device. - required: true - type: str - configured_password: - description: The password to be configured on the ICX device. - type: str - update_password: - description: - - This argument will instruct the module when to change the password. When - set to C(always), the password will always be updated in the device - and when set to C(on_create) the password will be updated only if - the username is created. - choices: ['on_create', 'always'] - type: str - privilege: - description: - - The privilege level to be granted to the user - choices: ['0', '4', '5'] - type: str - nopassword: - description: - - Defines the username without assigning - a password. This will allow the user to login to the system - without being authenticated by a password. - type: bool - state: - description: - - Configures the state of the username definition - as it relates to the device operational configuration. When set - to I(present), the username(s) should be configured in the device active - configuration and when set to I(absent) the username(s) should not be - in the device active configuration - choices: ['present', 'absent'] - type: str - access_time: - description: - - This parameter indicates the time the file's access time should be set to. - Should be preserve when no modification is required, YYYYMMDDHHMM.SS when using default time format, or now. - Default is None meaning that preserve is the default for state=[file,directory,link,hard] and now is default for state=touch - type: str - 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 overridden, by specifying it as module parameter. - type: bool - name: - description: - - The username to be configured on the ICX device. - required: true - type: str - configured_password: - description: The password to be configured on the ICX device. - type: str - update_password: - description: - - This argument will instruct the module when to change the password. When - set to C(always), the password will always be updated in the device - and when set to C(on_create) the password will be updated only if - the username is created. - default: always - choices: ['on_create', 'always'] - type: str - privilege: - description: - - The privilege level to be granted to the user - default: 0 - choices: ['0', '4', '5'] - type: str - nopassword: - description: - - Defines the username without assigning - a password. This will allow the user to login to the system - without being authenticated by a password. - type: bool - purge: - description: - - If set to true module will remove any previously - configured usernames on the device except the current defined set of users. - type: bool - default: false - state: - description: - - Configures the state of the username definition - as it relates to the device operational configuration. When set - to I(present), the username(s) should be configured in the device active - configuration and when set to I(absent) the username(s) should not be - in the device active configuration - default: present - choices: ['present', 'absent'] - type: str - access_time: - description: - - This parameter indicates the time the file's access time should be set to. - Should be preserve when no modification is required, YYYYMMDDHHMM.SS when using default time format, or now. - Default is None meaning that preserve is the default for state=[file,directory,link,hard] and now is default for state=touch - type: str - 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 overridden, by specifying it as module parameter. - type: bool - default: yes -""" - -EXAMPLES = """ -- name: create a new user without password - icx_user: - name: user1 - nopassword: true - -- name: create a new user with password - icx_user: - name: user1 - configured_password: 'newpassword' - -- name: remove users - icx_user: - name: user1 - state: absent - -- name: set user privilege level to 5 - icx_user: - name: user1 - privilege: 5 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - username ansible nopassword - - username ansible password-string alethea123 - - no username ansible - - username ansible privilege 5 - - username ansible enable -""" - -from copy import deepcopy - -import re -import base64 -import hashlib - -from functools import partial - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.network.common.utils import remove_default_spec -from ansible.module_utils.connection import exec_command -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.icx.icx import get_config, load_config - - -def get_param_value(key, item, module): - if not item.get(key): - value = module.params[key] - - else: - value_type = module.argument_spec[key].get('type', 'str') - type_checker = module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type] - type_checker(item[key]) - value = item[key] - - validator = globals().get('validate_%s' % key) - if all((value, validator)): - validator(value, module) - - return value - - -def map_params_to_obj(module): - users = module.params['aggregate'] - if not users: - if not module.params['name'] and module.params['purge']: - return list() - elif not module.params['name']: - module.fail_json(msg='username is required') - else: - aggregate = [{'name': module.params['name']}] - else: - aggregate = list() - for item in users: - if not isinstance(item, dict): - aggregate.append({'name': item}) - elif 'name' not in item: - module.fail_json(msg='name is required') - else: - aggregate.append(item) - - objects = list() - - for item in aggregate: - get_value = partial(get_param_value, item=item, module=module) - item['configured_password'] = get_value('configured_password') - item['nopassword'] = get_value('nopassword') - item['privilege'] = get_value('privilege') - item['state'] = get_value('state') - objects.append(item) - - return objects - - -def parse_privilege(data): - match = re.search(r'privilege (\S)', data, re.M) - if match: - return match.group(1) - - -def map_config_to_obj(module): - compare = module.params['check_running_config'] - data = get_config(module, flags=['| include username'], compare=compare) - - match = re.findall(r'(?:^(?:u|\s{2}u))sername (\S+)', data, re.M) - if not match: - return list() - - instances = list() - - for user in set(match): - regex = r'username %s .+$' % user - cfg = re.findall(regex, data, re.M) - cfg = '\n'.join(cfg) - obj = { - 'name': user, - 'state': 'present', - 'nopassword': 'nopassword' in cfg, - 'configured_password': None, - 'privilege': parse_privilege(cfg) - } - instances.append(obj) - - return instances - - -def map_obj_to_commands(updates, module): - commands = list() - state = module.params['state'] - update_password = module.params['update_password'] - - def needs_update(want, have, x): - return want.get(x) and (want.get(x) != have.get(x)) - - def add(command, want, x): - command.append('username %s %s' % (want['name'], x)) - for update in updates: - want, have = update - if want['state'] == 'absent': - commands.append(user_del_cmd(want['name'])) - - if needs_update(want, have, 'privilege'): - add(commands, want, 'privilege %s password %s' % (want['privilege'], want['configured_password'])) - else: - if needs_update(want, have, 'configured_password'): - if update_password == 'always' or not have: - add(commands, want, '%spassword %s' % ('privilege ' + str(have.get('privilege')) + - " " if have.get('privilege') is not None else '', want['configured_password'])) - - if needs_update(want, have, 'nopassword'): - if want['nopassword']: - add(commands, want, 'nopassword') - - if needs_update(want, have, 'access_time'): - add(commands, want, 'access-time %s' % want['access_time']) - - if needs_update(want, have, 'expiry_days'): - add(commands, want, 'expires %s' % want['expiry_days']) - - return commands - - -def update_objects(want, have): - updates = list() - for entry in want: - item = next((i for i in have if i['name'] == entry['name']), None) - - if all((item is None, entry['state'] == 'present')): - updates.append((entry, {})) - - elif all((have == [], entry['state'] == 'absent')): - for key, value in iteritems(entry): - if key not in ['update_password']: - updates.append((entry, item)) - break - elif item: - for key, value in iteritems(entry): - if key not in ['update_password']: - if value is not None and value != item.get(key): - updates.append((entry, item)) - break - return updates - - -def user_del_cmd(username): - return 'no username %s' % username - - -def main(): - """entry point for module execution - """ - element_spec = dict( - name=dict(), - - configured_password=dict(no_log=True), - nopassword=dict(type='bool', default=False), - update_password=dict(default='always', choices=['on_create', 'always']), - privilege=dict(type='str', choices=['0', '4', '5']), - access_time=dict(type='str'), - state=dict(default='present', choices=['present', 'absent']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec, aliases=['users', 'collection']), - purge=dict(type='bool', default=False) - ) - - argument_spec.update(element_spec) - - mutually_exclusive = [('name', 'aggregate')] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - result = {'changed': False} - exec_command(module, 'skip') - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands(update_objects(want, have), module) - - if module.params['purge']: - want_users = [x['name'] for x in want] - have_users = [x['name'] for x in have] - for item in set(have_users).difference(want_users): - if item != 'admin': - commands.append(user_del_cmd(item)) - - result["commands"] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/modules/network/icx/icx_vlan.py b/lib/ansible/modules/network/icx/icx_vlan.py deleted file mode 100644 index cb2a83d800..0000000000 --- a/lib/ansible/modules/network/icx/icx_vlan.py +++ /dev/null @@ -1,784 +0,0 @@ -#!/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': 'community'} - -DOCUMENTATION = """ ---- -module: icx_vlan -version_added: "2.9" -author: "Ruckus Wireless (@Commscope)" -short_description: Manage VLANs on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of VLANs - on ICX network devices. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - name: - description: - - Name of the VLAN. - type: str - vlan_id: - description: - - ID of the VLAN. Range 1-4094. - required: true - type: int - interfaces: - description: - - List of ethernet ports or LAGS to be added as access(untagged) ports to the vlan. - To add a range of ports use 'to' keyword. See the example. - suboptions: - name: - description: - - Name of the interface or lag - type: list - purge: - description: - - Purge interfaces not defined in the I(name) - type: bool - type: dict - tagged: - description: - - List of ethernet ports or LAGS to be added as trunk(tagged) ports to the vlan. - To add a range of ports use 'to' keyword. See the example. - suboptions: - name: - description: - - Name of the interface or lag - type: list - purge: - description: - - Purge interfaces not defined in the I(name) - type: bool - type: dict - ip_dhcp_snooping: - description: - - Enables DHCP snooping on a VLAN. - type: bool - ip_arp_inspection: - description: - - Enables dynamic ARP inspection on a VLAN. - type: bool - associated_interfaces: - description: - - This is a intent option and checks the operational state of the for given vlan C(name) - for associated interfaces. If the value in the C(associated_interfaces) does not match with - the operational state of vlan interfaces on device it will result in failure. - type: list - associated_tagged: - description: - - This is a intent option and checks the operational state of given vlan C(name) - for associated tagged ports and lags. If the value in the C(associated_tagged) does not match with - the operational state of vlan interfaces on device it will result in failure. - type: list - delay: - description: - - Delay the play should wait to check for declarative intent params values. - default: 10 - type: int - stp: - description: - - Enable spanning-tree 802-1w/rstp for this vlan. - suboptions: - type: - description: - - Specify the type of spanning-tree - type: str - default: 802-1w - choices: ['802-1w','rstp'] - priority: - description: - - Configures the priority of the bridge. The value ranges from - 0 through 65535. A lower numerical value means the bridge has - a higher priority. Thus, the highest priority is 0. The default is 32768. - type: str - enabled: - description: - - Manage the state(Enable/Disable) of the spanning_tree_802_1w in the current vlan - type: bool - type: dict - aggregate: - description: - - List of VLANs definitions. - type: list - suboptions: - name: - description: - - Name of the VLAN. - type: str - vlan_id: - description: - - ID of the VLAN. Range 1-4094. - required: true - type: str - ip_dhcp_snooping: - description: - - Enables DHCP snooping on a VLAN. - type: bool - ip_arp_inspection: - description: - - Enables dynamic ARP inspection on a VLAN. - type: bool - tagged: - description: - - List of ethernet ports or LAGS to be added as trunk(tagged) ports to the vlan. - To add a range of ports use 'to' keyword. See the example. - suboptions: - name: - description: - - Name of the interface or lag - type: list - purge: - description: - - Purge interfaces not defined in the I(name) - type: bool - type: dict - interfaces: - description: - - List of ethernet ports or LAGS to be added as access(untagged) ports to the vlan. - To add a range of ports use 'to' keyword. See the example. - suboptions: - name: - description: - - Name of the interface or lag - type: list - purge: - description: - - Purge interfaces not defined in the I(name) - type: bool - type: dict - delay: - description: - - Delay the play should wait to check for declarative intent params values. - type: int - stp: - description: - - Enable spanning-tree 802-1w/rstp for this vlan. - suboptions: - type: - description: - - Specify the type of spanning-tree - type: str - default: 802-1w - choices: ['802-1w','rstp'] - priority: - description: - - Configures the priority of the bridge. The value ranges from - 0 through 65535. A lower numerical value means the bridge has - a higher priority. Thus, the highest priority is 0. The default is 32768. - type: str - enabled: - description: - - Manage the state(Enable/Disable) of the spanning_tree_802_1w in the current vlan - type: bool - type: dict - state: - description: - - State of the VLAN configuration. - type: str - choices: ['present', 'absent'] - 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 overridden, by specifying it as module parameter. - type: bool - associated_interfaces: - description: - - This is a intent option and checks the operational state of the for given vlan C(name) - for associated interfaces. If the value in the C(associated_interfaces) does not match with - the operational state of vlan interfaces on device it will result in failure. - type: list - associated_tagged: - description: - - This is a intent option and checks the operational state of given vlan C(name) - for associated tagged ports and lags. If the value in the C(associated_tagged) does not match with - the operational state of vlan interfaces on device it will result in failure. - type: list - purge: - description: - - Purge VLANs not defined in the I(aggregate) parameter. - default: no - type: bool - state: - description: - - State of the VLAN configuration. - type: str - default: present - choices: ['present', 'absent'] - 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 overridden, by specifying it as module parameter. - type: bool - default: yes -""" - -EXAMPLES = """ -- name: Add a single ethernet 1/1/48 as access(untagged) port to vlan 20 - icx_vlan: - name: test-vlan - vlan_id: 20 - interfaces: - name: - - ethernet 1/1/48 - -- name: Add a single LAG 10 as access(untagged) port to vlan 20 - icx_vlan: - vlan_id: 20 - interfaces: - name: - - lag 10 - -- name: Add a range of ethernet ports as trunk(tagged) ports to vlan 20 by port - icx_vlan: - vlan_id: 20 - tagged: - name: - - ethernet 1/1/40 to 1/1/48 - -- name: Add discontinuous lags, ethernet ports as access(untagged) and trunk(tagged) port to vlan 20. - icx_vlan: - vlan_id: 20 - interfaces: - name: - - ethernet 1/1/40 to 1/1/48 - - ethernet 2/1/1 - - lag 1 - - lag 3 to 5 - tagged: - name: - - ethernet 1/1/20 to 1/1/25 - - lag 1 to 3 - -- name: Remove an access and range of trunk ports from vlan - icx_vlan: - vlan_id: 20 - interfaces: - name: - - ethernet 1/1/40 - tagged: - name: - - ethernet 1/1/39 to 1/1/70 - -- name: Enable dhcp snooping, disable arp inspection in vlan - icx_vlan: - vlan_id: 20 - ip_dhcp_snooping: present - ip_arp_inspection: absent - -- name: Create vlan 20. Enable arp inspection in vlan. Purge all other vlans. - icx_vlan: - vlan_id: 20 - ip_arp_inspection: present - purge: present - -- name: Remove vlan 20. - icx_vlan: - vlan_id: 20 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - vlan 100 - - name test-vlan -""" - -import re -from time import sleep -import itertools -from copy import deepcopy -from time import sleep -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.network.common.config import NetworkConfig -from ansible.module_utils.network.icx.icx import load_config, get_config -from ansible.module_utils.connection import Connection, ConnectionError, exec_command -from ansible.module_utils.network.common.utils import conditional, remove_default_spec - - -def search_obj_in_list(vlan_id, lst): - obj = list() - for o in lst: - if str(o['vlan_id']) == vlan_id: - return o - - -def parse_vlan_brief(module, vlan_id): - command = 'show run vlan %s' % vlan_id - rc, out, err = exec_command(module, command) - lines = out.split('\n') - untagged_ports = list() - untagged_lags = list() - tagged_ports = list() - tagged_lags = list() - - for line in lines: - if 'tagged' in line.split(): - lags = line.split(" lag ") - ports = lags[0].split(" ethe ") - del ports[0] - del lags[0] - for port in ports: - if "to" in port: - p = port.split(" to ") - pr = int(p[1].split('/')[2]) - int(p[0].split('/')[2]) - for i in range(0, pr + 1): - tagged_ports.append((int(p[0].split('/')[2]) + i)) - else: - tagged_ports.append(int(port.split('/')[2])) - for lag in lags: - if "to" in lag: - l = lag.split(" to ") - lr = int(l[1]) - int(l[0]) - for i in range(0, lr + 1): - tagged_lags.append((int(l[0]) + i)) - else: - tagged_lags.append(int(lag)) - if 'untagged' in line.split(): - lags = line.split(" lag ") - ports = lags[0].split(" ethe ") - del ports[0] - del lags[0] - for port in ports: - if "to" in port: - p = port.split(" to ") - pr = int(p[1].split('/')[2]) - int(p[0].split('/')[2]) - for i in range(0, pr + 1): - untagged_ports.append((int(p[0].split('/')[2]) + i)) - else: - untagged_ports.append(int(port.split('/')[2])) - for lag in lags: - if "to" in lag: - l = lag.split(" to ") - lr = int(l[1]) - int(l[0]) - for i in range(0, lr + 1): - untagged_lags.append((int(l[0]) + i)) - else: - untagged_lags.append(int(lag)) - - return untagged_ports, untagged_lags, tagged_ports, tagged_lags - - -def extract_list_from_interface(interface): - if 'ethernet' in interface: - if 'to' in interface: - s = re.search(r"\d+\/\d+/(?P<low>\d+)\sto\s+\d+\/\d+/(?P<high>\d+)", interface) - low = int(s.group('low')) - high = int(s.group('high')) - else: - s = re.search(r"\d+\/\d+/(?P<low>\d+)", interface) - low = int(s.group('low')) - high = int(s.group('low')) - elif 'lag' in interface: - if 'to' in interface: - s = re.search(r"(?P<low>\d+)\sto\s(?P<high>\d+)", interface) - low = int(s.group('low')) - high = int(s.group('high')) - else: - s = re.search(r"(?P<low>\d+)", interface) - low = int(s.group('low')) - high = int(s.group('low')) - - return low, high - - -def parse_vlan_id(module): - vlans = [] - command = 'show vlan brief' - rc, out, err = exec_command(module, command) - lines = out.split('\n') - for line in lines: - if 'VLANs Configured :' in line: - values = line.split(':')[1] - vlans = [s for s in values.split() if s.isdigit()] - s = re.findall(r"(?P<low>\d+)\sto\s(?P<high>\d+)", values) - for ranges in s: - low = int(ranges[0]) + 1 - high = int(ranges[1]) - while(high > low): - vlans.append(str(low)) - low = low + 1 - return vlans - - -def spanning_tree(module, stp): - stp_cmd = list() - if stp.get('enabled') is False: - if stp.get('type') == '802-1w': - stp_cmd.append('no spanning-tree' + ' ' + stp.get('type')) - stp_cmd.append('no spanning-tree') - - elif stp.get('type'): - stp_cmd.append('spanning-tree' + ' ' + stp.get('type')) - if stp.get('priority') and stp.get('type') == 'rstp': - module.fail_json(msg='spanning-tree 802-1w only can have priority') - elif stp.get('priority'): - stp_cmd.append('spanning-tree' + ' ' + stp.get('type') + ' ' + 'priority' + ' ' + stp.get('priority')) - - return stp_cmd - - -def map_params_to_obj(module): - obj = [] - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - stp = item.get('stp') - if stp: - stp_cmd = spanning_tree(module, stp) - item.update({'stp': stp_cmd}) - - d = item.copy() - - obj.append(d) - - else: - params = { - 'name': module.params['name'], - 'vlan_id': module.params['vlan_id'], - 'interfaces': module.params['interfaces'], - 'tagged': module.params['tagged'], - 'associated_interfaces': module.params['associated_interfaces'], - 'associated_tagged': module.params['associated_tagged'], - 'delay': module.params['delay'], - 'ip_dhcp_snooping': module.params['ip_dhcp_snooping'], - 'ip_arp_inspection': module.params['ip_arp_inspection'], - 'state': module.params['state'], - } - - stp = module.params.get('stp') - if stp: - stp_cmd = spanning_tree(module, stp) - params.update({'stp': stp_cmd}) - - obj.append(params) - - return obj - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - purge = module.params['purge'] - - for w in want: - vlan_id = w['vlan_id'] - state = w['state'] - name = w['name'] - interfaces = w.get('interfaces') - tagged = w.get('tagged') - dhcp = w.get('ip_dhcp_snooping') - arp = w.get('ip_arp_inspection') - stp = w.get('stp') - obj_in_have = search_obj_in_list(str(vlan_id), have) - - if state == 'absent': - if have == []: - commands.append('no vlan {0}'.format(vlan_id)) - if obj_in_have: - commands.append('no vlan {0}'.format(vlan_id)) - - elif state == 'present': - if not obj_in_have: - commands.append('vlan {0}'.format(vlan_id)) - if name: - commands.append('vlan {0} name {1}'.format(vlan_id, name)) - - if interfaces: - if interfaces['name']: - for item in interfaces['name']: - commands.append('untagged {0}'.format(item)) - - if tagged: - if tagged['name']: - for item in tagged['name']: - commands.append('tagged {0}'.format(item)) - - if dhcp is True: - commands.append('ip dhcp snooping vlan {0}'.format(vlan_id)) - elif dhcp is False: - commands.append('no ip dhcp snooping vlan {0}'.format(vlan_id)) - - if arp is True: - commands.append('ip arp inspection vlan {0}'.format(vlan_id)) - elif dhcp is False: - commands.append('no ip arp inspection vlan {0}'.format(vlan_id)) - - if stp: - if w.get('stp'): - [commands.append(cmd) for cmd in w['stp']] - - else: - commands.append('vlan {0}'.format(vlan_id)) - if name: - if name != obj_in_have['name']: - commands.append('vlan {0} name {1}'.format(vlan_id, name)) - - if interfaces: - if interfaces['name']: - have_interfaces = list() - for interface in interfaces['name']: - low, high = extract_list_from_interface(interface) - - while(high >= low): - if 'ethernet' in interface: - have_interfaces.append('ethernet 1/1/{0}'.format(low)) - if 'lag' in interface: - have_interfaces.append('lag {0}'.format(low)) - low = low + 1 - - if interfaces['purge'] is True: - remove_interfaces = list(set(obj_in_have['interfaces']) - set(have_interfaces)) - for item in remove_interfaces: - commands.append('no untagged {0}'.format(item)) - - if interfaces['name']: - add_interfaces = list(set(have_interfaces) - set(obj_in_have['interfaces'])) - for item in add_interfaces: - commands.append('untagged {0}'.format(item)) - - if tagged: - if tagged['name']: - have_tagged = list() - for tag in tagged['name']: - low, high = extract_list_from_interface(tag) - - while(high >= low): - if 'ethernet' in tag: - have_tagged.append('ethernet 1/1/{0}'.format(low)) - if 'lag' in tag: - have_tagged.append('lag {0}'.format(low)) - low = low + 1 - if tagged['purge'] is True: - remove_tagged = list(set(obj_in_have['tagged']) - set(have_tagged)) - for item in remove_tagged: - commands.append('no tagged {0}'.format(item)) - - if tagged['name']: - add_tagged = list(set(have_tagged) - set(obj_in_have['tagged'])) - for item in add_tagged: - commands.append('tagged {0}'.format(item)) - - if dhcp != obj_in_have['ip_dhcp_snooping']: - if dhcp is True: - commands.append('ip dhcp snooping vlan {0}'.format(vlan_id)) - elif dhcp is False: - commands.append('no ip dhcp snooping vlan {0}'.format(vlan_id)) - - if arp != obj_in_have['ip_arp_inspection']: - if arp is True: - commands.append('ip arp inspection vlan {0}'.format(vlan_id)) - elif arp is False: - commands.append('no ip arp inspection vlan {0}'.format(vlan_id)) - - if stp: - if w.get('stp'): - [commands.append(cmd) for cmd in w['stp']] - - if len(commands) == 1 and 'vlan ' + str(vlan_id) in commands: - commands = [] - - if purge: - commands = [] - vlans = parse_vlan_id(module) - for h in vlans: - obj_in_want = search_obj_in_list(h, want) - if not obj_in_want and h != '1': - commands.append('no vlan {0}'.format(h)) - - return commands - - -def parse_name_argument(module, item): - command = 'show vlan {0}'.format(item) - rc, out, err = exec_command(module, command) - match = re.search(r"Name (\S+),", out) - if match: - return match.group(1) - - -def parse_interfaces_argument(module, item, port_type): - untagged_ports, untagged_lags, tagged_ports, tagged_lags = parse_vlan_brief(module, item) - ports = list() - if port_type == "interfaces": - if untagged_ports: - for port in untagged_ports: - ports.append('ethernet 1/1/' + str(port)) - if untagged_lags: - for port in untagged_lags: - ports.append('lag ' + str(port)) - - elif port_type == "tagged": - if tagged_ports: - for port in tagged_ports: - ports.append('ethernet 1/1/' + str(port)) - if tagged_lags: - for port in tagged_lags: - ports.append('lag ' + str(port)) - - return ports - - -def parse_config_argument(config, arg): - match = re.search(arg, config, re.M) - if match: - return True - else: - return False - - -def map_config_to_obj(module): - config = get_config(module) - vlans = parse_vlan_id(module) - instance = list() - - for item in set(vlans): - obj = { - 'vlan_id': item, - 'name': parse_name_argument(module, item), - 'interfaces': parse_interfaces_argument(module, item, 'interfaces'), - 'tagged': parse_interfaces_argument(module, item, 'tagged'), - 'ip_dhcp_snooping': parse_config_argument(config, 'ip dhcp snooping vlan {0}'.format(item)), - 'ip_arp_inspection': parse_config_argument(config, 'ip arp inspection vlan {0}'.format(item)), - } - instance.append(obj) - return instance - - -def check_fail(module, output): - error = [ - re.compile(r"^error", re.I) - ] - for x in output: - for regex in error: - if regex.search(x): - module.fail_json(msg=x) - - -def check_declarative_intent_params(want, module, result): - def parse_ports(interfaces, ports, lags): - for interface in interfaces: - low, high = extract_list_from_interface(interface) - - while(high >= low): - if 'ethernet' in interface: - if not (low in ports): - module.fail_json(msg='One or more conditional statements have not been satisfied ' + interface) - if 'lag' in interface: - if not (low in lags): - module.fail_json(msg='One or more conditional statements have not been satisfied ' + interface) - low = low + 1 - - is_delay = False - low = 0 - high = 0 - for w in want: - if w.get('associated_interfaces') is None and w.get('associated_tagged') is None: - continue - - if result['changed'] and not is_delay: - sleep(module.params['delay']) - is_delay = True - - untagged_ports, untagged_lags, tagged_ports, tagged_lags = parse_vlan_brief(module, w['vlan_id']) - - if w['associated_interfaces']: - parse_ports(w.get('associated_interfaces'), untagged_ports, untagged_lags) - - if w['associated_tagged']: - parse_ports(w.get('associated_tagged'), tagged_ports, tagged_lags) - - -def main(): - """ main entry point for module execution - """ - stp_spec = dict( - type=dict(default='802-1w', choices=['802-1w', 'rstp']), - priority=dict(), - enabled=dict(type='bool'), - ) - inter_spec = dict( - name=dict(type='list'), - purge=dict(type='bool') - ) - tagged_spec = dict( - name=dict(type='list'), - purge=dict(type='bool') - ) - element_spec = dict( - vlan_id=dict(type='int'), - name=dict(), - interfaces=dict(type='dict', options=inter_spec), - tagged=dict(type='dict', options=tagged_spec), - ip_dhcp_snooping=dict(type='bool'), - ip_arp_inspection=dict(type='bool'), - associated_interfaces=dict(type='list'), - associated_tagged=dict(type='list'), - delay=dict(default=10, type='int'), - stp=dict(type='dict', options=stp_spec), - state=dict(default='present', choices=['present', 'absent']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - aggregate_spec = deepcopy(element_spec) - aggregate_spec['vlan_id'] = dict(required=True) - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - purge=dict(default=False, type='bool') - ) - argument_spec.update(element_spec) - required_one_of = [['vlan_id', 'aggregate']] - mutually_exclusive = [['vlan_id', 'aggregate']] - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - warnings = list() - result = {} - result['changed'] = False - if warnings: - result['warnings'] = warnings - exec_command(module, 'skip') - want = map_params_to_obj(module) - if module.params['check_running_config'] is False: - have = [] - else: - have = map_config_to_obj(module) - commands = map_obj_to_commands((want, have), module) - result['commands'] = commands - - if commands: - if not module.check_mode: - output = load_config(module, commands) - if output: - check_fail(module, output) - result['output'] = output - result['changed'] = True - - check_declarative_intent_params(want, module, result) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() |