diff options
Diffstat (limited to 'ansible_collections/dellemc/os10/plugins')
18 files changed, 0 insertions, 2961 deletions
diff --git a/ansible_collections/dellemc/os10/plugins/action/os10.py b/ansible_collections/dellemc/os10/plugins/action/os10.py deleted file mode 100644 index 5669001c0..000000000 --- a/ansible_collections/dellemc/os10/plugins/action/os10.py +++ /dev/null @@ -1,94 +0,0 @@ -# -# (c) 2020 Red Hat Inc. -# -# (c) 2020 Dell Inc. -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see <http://www.gnu.org/licenses/>. -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import sys -import copy - - -from ansible import constants as C -from ansible.module_utils._text import to_text -from ansible.module_utils.connection import Connection -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import load_provider -from ansible_collections.dellemc.os10.plugins.module_utils.network.os10 import os10_provider_spec -from ansible.utils.display import Display - -display = Display() - - -class ActionModule(ActionNetworkModule): - - def run(self, tmp=None, task_vars=None): - del tmp # tmp no longer has any effect - - self._config_module = True if self._task.action == 'os10_config' else False - socket_path = None - - if self._play_context.connection == 'network_cli': - provider = self._task.args.get('provider', {}) - if any(provider.values()): - display.warning('provider is unnecessary when using network_cli and will be ignored') - del self._task.args['provider'] - elif self._play_context.connection == 'local': - provider = load_provider(os10_provider_spec, self._task.args) - pc = copy.deepcopy(self._play_context) - pc.connection = 'network_cli' - pc.network_os = 'dellemc.os10.os10' - pc.remote_addr = provider['host'] or self._play_context.remote_addr - pc.port = int(provider['port'] or self._play_context.port or 22) - pc.remote_user = provider['username'] or self._play_context.connection_user - pc.password = provider['password'] or self._play_context.password - pc.private_key_file = provider['ssh_keyfile'] or self._play_context.private_key_file - command_timeout = int(provider['timeout'] or C.PERSISTENT_COMMAND_TIMEOUT) - pc.become = provider['authorize'] or False - if pc.become: - pc.become_method = 'enable' - pc.become_pass = provider['auth_pass'] - - display.vvv('using connection plugin %s' % pc.connection, pc.remote_addr) - connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin) - connection.set_options(direct={'persistent_command_timeout': command_timeout}) - - socket_path = connection.run() - display.vvvv('socket_path: %s' % socket_path, pc.remote_addr) - if not socket_path: - return {'failed': True, - 'msg': 'unable to open shell. Please see: ' + - 'https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell'} - - task_vars['ansible_socket'] = socket_path - - # make sure we are in the right cli context which should be - # enable mode and not config module - if socket_path is None: - socket_path = self._connection.socket_path - - conn = Connection(socket_path) - out = conn.get_prompt() - while to_text(out, errors='surrogate_then_replace').strip().endswith(')#'): - display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr) - conn.send_command('exit') - out = conn.get_prompt() - - result = super(ActionModule, self).run(task_vars=task_vars) - return result diff --git a/ansible_collections/dellemc/os10/plugins/action/textfsm_parser.py b/ansible_collections/dellemc/os10/plugins/action/textfsm_parser.py deleted file mode 100644 index 602186c89..000000000 --- a/ansible_collections/dellemc/os10/plugins/action/textfsm_parser.py +++ /dev/null @@ -1,81 +0,0 @@ -# -*- coding: utf-8 -*- - -# (c) 2020, Ansible by Red Hat, inc -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see <http://www.gnu.org/licenses/>. -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -from ansible.module_utils.six import StringIO, string_types - -from ansible.plugins.action import ActionBase -from ansible.errors import AnsibleError - -try: - import textfsm - HAS_TEXTFSM = True -except ImportError: - HAS_TEXTFSM = False - - -class ActionModule(ActionBase): - - def run(self, tmp=None, task_vars=None): - ''' handler for textfsm action ''' - - if task_vars is None: - task_vars = dict() - - result = super(ActionModule, self).run(tmp, task_vars) - del tmp # tmp no longer has any effect - - try: - if not HAS_TEXTFSM: - raise AnsibleError('textfsm_parser engine requires the TextFSM library to be installed') - - try: - filename = self._task.args.get('file') - src = self._task.args.get('src') - content = self._task.args['content'] - name = self._task.args.get('name') - except KeyError as exc: - raise AnsibleError('missing required argument: %s' % exc) - - if src and filename: - raise AnsibleError('`src` and `file` are mutually exclusive arguments') - - if not isinstance(content, string_types): - return {'failed': True, 'msg': '`content` must be of type str, got %s' % type(content)} - - if filename: - tmpl = open(filename) - else: - tmpl = StringIO() - tmpl.write(src.strip()) - tmpl.seek(0) - - try: - re_table = textfsm.TextFSM(tmpl) - fsm_results = re_table.ParseText(content) - - except Exception as exc: - raise AnsibleError(str(exc)) - - final_facts = [] - for item in fsm_results: - facts = {} - facts.update(dict(zip(re_table.header, item))) - final_facts.append(facts) - - if name: - result['ansible_facts'] = {name: final_facts} - else: - result['ansible_facts'] = {} - - finally: - self._remove_tmp_path(self._connection._shell.tmpdir) - - return result diff --git a/ansible_collections/dellemc/os10/plugins/cliconf/os10.py b/ansible_collections/dellemc/os10/plugins/cliconf/os10.py deleted file mode 100644 index 7d009f5a2..000000000 --- a/ansible_collections/dellemc/os10/plugins/cliconf/os10.py +++ /dev/null @@ -1,88 +0,0 @@ -# -# (c) 2020 Red Hat Inc. -# -# (c) 2020 Dell Inc. -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see <http://www.gnu.org/licenses/>. -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = """ ---- -cliconf: os10 -short_description: Use os10 cliconf to run command on Dell OS10 platform -description: - - This os10 plugin provides low level abstraction apis for - sending and receiving CLI commands from Dell OS10 network devices. -""" - -import re -import json - -from itertools import chain - -from ansible.module_utils._text import to_bytes, to_text -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list -from ansible.plugins.cliconf import CliconfBase, enable_mode - - -class Cliconf(CliconfBase): - - def get_device_info(self): - device_info = {} - - device_info['network_os'] = 'dellemc.os10.os10' - reply = self.get('show version') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'OS Version (\S+)', data) - if match: - device_info['network_os_version'] = match.group(1) - - match = re.search(r'System Type (\S+)', data, re.M) - if match: - device_info['network_os_model'] = match.group(1) - - reply = self.get('show running-configuration | grep hostname') - data = to_text(reply, errors='surrogate_or_strict').strip() - match = re.search(r'^hostname (.+)', data, re.M) - if match: - device_info['network_os_hostname'] = match.group(1) - - return device_info - - @enable_mode - def get_config(self, source='running', format='text', flags=None): - if source not in ('running', 'startup'): - return self.invalid_params("fetching configuration from %s is not supported" % source) - if source == 'running': - cmd = 'show running-config all' - else: - cmd = 'show startup-config' - return self.send_command(cmd) - - @enable_mode - def edit_config(self, command): - for cmd in chain(['configure terminal'], to_list(command), ['end']): - self.send_command(to_bytes(cmd)) - - def get(self, command, prompt=None, answer=None, sendonly=False, newline=True, check_all=False): - return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, newline=newline, check_all=check_all) - - def get_capabilities(self): - result = super(Cliconf, self).get_capabilities() - return json.dumps(result) diff --git a/ansible_collections/dellemc/os10/plugins/doc_fragments/os10.py b/ansible_collections/dellemc/os10/plugins/doc_fragments/os10.py deleted file mode 100644 index 9a6baf44c..000000000 --- a/ansible_collections/dellemc/os10/plugins/doc_fragments/os10.py +++ /dev/null @@ -1,75 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Peter Sprygada <psprygada@ansible.com> -# Copyright: (c) 2020, Dell Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import (absolute_import, division, print_function) - -__metaclass__ = type - - -class ModuleDocFragment(object): - - # Standard files documentation fragment - DOCUMENTATION = r''' -options: - provider: - description: - - A dict object containing connection details. - type: dict - suboptions: - host: - description: - - Specifies the DNS host name or address for connecting to the remote - device over the specified transport. The value of host is used as - the destination address for the transport. - type: str - port: - description: - - Specifies the port to use when building the connection to the remote - device. - type: int - username: - description: - - User to authenticate the SSH session to the remote device. If the - value is not specified in the task, the value of environment variable - C(ANSIBLE_NET_USERNAME) will be used instead. - type: str - password: - description: - - Password to authenticate the SSH session to the remote device. If the - value is not specified in the task, the value of environment variable - C(ANSIBLE_NET_PASSWORD) will be used instead. - type: str - ssh_keyfile: - description: - - Path to an ssh key used to authenticate the SSH session to the remote - device. If the value is not specified in the task, the value of - environment variable C(ANSIBLE_NET_SSH_KEYFILE) will be used instead. - type: path - timeout: - description: - - Specifies idle timeout (in seconds) for the connection. Useful if the - console freezes before continuing. For example when saving - configurations. - type: int - authorize: - description: - - Instructs the module to enter privileged mode on the remote device before - sending any commands. If not specified, the device will attempt to execute - all commands in non-privileged mode. If the value is not specified in the - task, the value of environment variable C(ANSIBLE_NET_AUTHORIZE) will be - used instead. - type: bool - default: false - auth_pass: - description: - - Specifies the password to use if required to enter privileged mode on the - remote device. If I(authorize) is false, then this argument does nothing. - If the value is not specified in the task, the value of environment variable - C(ANSIBLE_NET_AUTH_PASS) will be used instead. - type: str -notes: - - For more information on using Ansible to manage Dell EMC Network devices see U(https://www.ansible.com/ansible-dell-networking). -''' diff --git a/ansible_collections/dellemc/os10/plugins/module_utils/network/__init__.py b/ansible_collections/dellemc/os10/plugins/module_utils/network/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/ansible_collections/dellemc/os10/plugins/module_utils/network/__init__.py +++ /dev/null diff --git a/ansible_collections/dellemc/os10/plugins/module_utils/network/base_network_show.py b/ansible_collections/dellemc/os10/plugins/module_utils/network/base_network_show.py deleted file mode 100644 index b287c38c9..000000000 --- a/ansible_collections/dellemc/os10/plugins/module_utils/network/base_network_show.py +++ /dev/null @@ -1,42 +0,0 @@ -from __future__ import (absolute_import, division, print_function) -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from collections import OrderedDict -import traceback - -LIB_IMP_ERR = None -ERR_MSG = None -try: - import xmltodict - import yaml - HAS_LIB = True -except Exception as e: - HAS_LIB = False - ERR_MSG = to_native(e) - LIB_IMP_ERR = traceback.format_exc() - -__copyright__ = "(c) Copyright 2020 Dell Inc. or its subsidiaries. All rights reserved." -__metaclass__ = type - - -class BaseNetworkShow(object): - """The goal of this class is to extended by other in order to implement show system network view ansible modules""" - - def __init__(self): - self.module = AnsibleModule(argument_spec=self.get_fields()) - if not HAS_LIB: - self.module.fail_json( - msg=ERR_MSG, - exception=LIB_IMP_ERR) - self.exit_msg = OrderedDict() - - def xml_to_dict(self, value): - - return xmltodict.parse(value) - - def dict_to_yaml(self, value): - return yaml.safe_dump(value, default_flow_style=False) - - -if __name__ == '__main__': - pass diff --git a/ansible_collections/dellemc/os10/plugins/module_utils/network/os10.py b/ansible_collections/dellemc/os10/plugins/module_utils/network/os10.py deleted file mode 100644 index 35976488d..000000000 --- a/ansible_collections/dellemc/os10/plugins/module_utils/network/os10.py +++ /dev/null @@ -1,146 +0,0 @@ -# -# (c) 2020 Peter Sprygada, <psprygada@ansible.com> -# (c) 2020 Red Hat, Inc -# -# Copyright (c) 2020 Dell Inc. -# -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -from __future__ import (absolute_import, division, print_function) - -import re - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback -from ansible.module_utils.connection import exec_command -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, ConfigLine - -__metaclass__ = type - -_DEVICE_CONFIGS = {} - -WARNING_PROMPTS_RE = [ - r"[\r\n]?\[confirm yes/no\]:\s?$", - r"[\r\n]?\[y/n\]:\s?$", - r"[\r\n]?\[yes/no\]:\s?$" -] - -os10_provider_spec = { - 'host': dict(), - 'port': dict(type='int'), - 'username': dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])), - 'password': dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True), - 'ssh_keyfile': dict(fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE']), type='path'), - 'authorize': dict(fallback=(env_fallback, ['ANSIBLE_NET_AUTHORIZE']), type='bool'), - 'auth_pass': dict(fallback=(env_fallback, ['ANSIBLE_NET_AUTH_PASS']), no_log=True), - 'timeout': dict(type='int'), -} -os10_argument_spec = { - 'provider': dict(type='dict', options=os10_provider_spec), -} - - -def check_args(module, warnings): - pass - - -def get_config(module, flags=None): - flags = [] if flags is None else flags - - cmd = 'show running-configuration' - cmd += ' '.join(flags) - cmd = cmd.strip() - - try: - return _DEVICE_CONFIGS[cmd] - except KeyError: - rc, out, err = exec_command(module, cmd) - if rc != 0: - module.fail_json(msg='unable to retrieve current config', stderr=to_text(err, errors='surrogate_or_strict')) - cfg = to_text(out, errors='surrogate_or_strict').strip() - _DEVICE_CONFIGS[cmd] = cfg - return cfg - - -def to_commands(module, commands): - spec = { - 'command': dict(key=True), - 'prompt': dict(), - 'answer': dict() - } - transform = ComplexList(spec, module) - return transform(commands) - - -def run_commands(module, commands, check_rc=True): - responses = list() - commands = to_commands(module, to_list(commands)) - for cmd in commands: - cmd = module.jsonify(cmd) - rc, out, err = exec_command(module, cmd) - if check_rc and rc != 0: - module.fail_json(msg=to_text(err, errors='surrogate_or_strict'), rc=rc) - responses.append(to_text(out, errors='surrogate_or_strict')) - return responses - - -def load_config(module, commands): - rc, out, err = exec_command(module, 'configure terminal') - if rc != 0: - module.fail_json(msg='unable to enter configuration mode', err=to_text(err, errors='surrogate_or_strict')) - - commands.append('commit') - for command in to_list(commands): - if command == 'end': - continue - rc, out, err = exec_command(module, command) - if rc != 0: - module.fail_json(msg=to_text(err, errors='surrogate_or_strict'), command=command, rc=rc) - - exec_command(module, 'end') - - -def get_sublevel_config(running_config, module): - contents = list() - current_config_contents = list() - running_config = NetworkConfig(contents=running_config, indent=1) - obj = running_config.get_object(module.params['parents']) - if obj: - contents = obj.children - contents[:0] = module.params['parents'] - - indent = 0 - for c in contents: - if isinstance(c, str): - current_config_contents.append(c.rjust(len(c) + indent, ' ')) - if isinstance(c, ConfigLine): - current_config_contents.append(c.raw) - indent = 1 - sublevel_config = '\n'.join(current_config_contents) - - return sublevel_config diff --git a/ansible_collections/dellemc/os10/plugins/modules/__init__.py b/ansible_collections/dellemc/os10/plugins/modules/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/ansible_collections/dellemc/os10/plugins/modules/__init__.py +++ /dev/null diff --git a/ansible_collections/dellemc/os10/plugins/modules/base_xml_to_dict.py b/ansible_collections/dellemc/os10/plugins/modules/base_xml_to_dict.py deleted file mode 100644 index b7d82f774..000000000 --- a/ansible_collections/dellemc/os10/plugins/modules/base_xml_to_dict.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/python - -# 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) - -__copyright__ = "(c) Copyright 2020 Dell Inc. or its subsidiaries. All rights reserved." - -__metaclass__ = type - - -DOCUMENTATION = ''' -module: base_xml_to_dict -author: "Senthil Kumar Ganesan (@skg-net)" -short_description: Operations for show command output convertion from xml to json format. -description: - - - Get the show system inforamtion of a Leaf-Spine. - -options: - cli_responses: - type: str - description: - - show command xml output - required: True -''' -EXAMPLES = ''' -Copy below YAML into a playbook (e.g. play.yml) and run as follows: - -#$ ansible-playbook -i inv play.yml -name: setup the plabook to get show command output in dict format -hosts: localhost -connection: local -gather_facts: False -vars: - cli: - username: admin - password: admin -tasks: -- name: "Get Dell EMC OS10 Show output in dict format" - os10_command: - commands: "{{ command_list }}" - register: show -- debug: var=show -- name: call to lib to get output in dict - base_xml_to_dict: - cli_responses: "{{ item }}" - loop: "{{ show.stdout }}" -''' - -from ansible.module_utils._text import to_native -from ansible.module_utils.basic import AnsibleModule -from collections import OrderedDict -import traceback - -LIB_IMP_ERR = None -ERR_MSG = None -try: - import xmltodict - HAS_LIB = True -except Exception as e: - HAS_LIB = False - ERR_MSG = to_native(e) - LIB_IMP_ERR = traceback.format_exc() - - -class XmlToDictAnsibleModule(object): - """The goal of this class is to convert xml input to dict""" - - def __init__(self): - self.module = AnsibleModule(argument_spec=self.get_fields()) - self.cli_responses = self.module.params['cli_responses'] - self.exit_msg = OrderedDict() - - def get_fields(self): - """Return valid fields""" - base_fields = { - 'cli_responses': { - 'type': 'str', - 'required': True - } - } - return base_fields - - def build_xml_list(self, xml_output): - xml_str_list = [] - xml_declaration_tag = '<?xml version="1.0"?>\n' - for data in xml_output.split('<?xml version="1.0"'): - if not data: - continue - xml_data = ''.join(data.splitlines(True)[1:]) - xml_str_list.append(xml_declaration_tag + xml_data) - - return xml_str_list - - def perform_action(self): - try: - out = list() - # the below line should be removed or not valid when the password - # decrypt issue is resolved - self.cli_responses = self.cli_responses.replace( - "*-", '').replace("*", '') - xml_str_list = self.build_xml_list(self.cli_responses) - for xml_list in xml_str_list: - out.append(xmltodict.parse(xml_list)) - - self.exit_msg.update({"result": out}) - self.module.exit_json(changed=False, msg=self.exit_msg) - except Exception as e: - self.module.fail_json( - msg=to_native(e), - exception=traceback.format_exc()) - - -def main(): - module_instance = XmlToDictAnsibleModule() - if not HAS_LIB: - module_instance.module.fail_json(msg=ERR_MSG, - exception=LIB_IMP_ERR) - module_instance.perform_action() - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/dellemc/os10/plugins/modules/bgp_validate.py b/ansible_collections/dellemc/os10/plugins/modules/bgp_validate.py deleted file mode 100644 index 85832c786..000000000 --- a/ansible_collections/dellemc/os10/plugins/modules/bgp_validate.py +++ /dev/null @@ -1,303 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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) - -__copyright__ = "(c) 2020 Dell Inc. or its subsidiaries. All rights reserved." - -__metaclass__ = type - -DOCUMENTATION = ''' -module: bgp_validate -author: "Senthil Kumar Ganesan (@skg-net)" -short_description: Validate the bgp neighbor state,raise error if it is not in established state -description: - - - Troubleshoot the bgp neighor state info using show ip bgp summary and show ip interface brief. - -options: - show_ip_bgp: - description: - - show ip bgp summary output - type: 'list' - required: True - show_ip_intf_brief: - description: - - show ip interface brief output - type: 'list' - required: True - bgp_neighbors: - description: - - planned neighbours input from group_var to compare actual - type: 'list' - required: True -''' -EXAMPLES = ''' -Copy below YAML into a playbook (e.g. play.yml) and run as follows: - -#$ ansible-playbook -i inv play.yml -name: Validate BGP configuration -hosts: localhost -connection: local -gather_facts: False -tasks: - - name: "Get Dell EMC OS10 Show ip bgp summary" - os10_command: - commands: - - command: "show ip bgp summary | display-xml" - - command: "show ip interface brief | display-xml" - provider: "{{ hostvars[item].cli }}" - with_items: "{{ groups['all'] }}" - register: show_bgp - - set_fact: - output_bgp: "{{ output_bgp|default([])+ [{'host': item.invocation.module_args.provider.host, 'inv_name': item.item, - 'stdout_show_bgp': item.stdout.0, 'stdout_show_ip': item.stdout.1}] }}" - loop: "{{ show_bgp.results }}" - - debug: var=output_bgp - - local_action: copy content={{ output_bgp }} dest=show - - name: call lib to convert bgp info from xml to dict format - base_xml_to_dict: - cli_responses: "{{ item.stdout_show_bgp }}" - with_items: - - "{{ output_bgp }}" - register: show_bgp_list - - name: call lib to convert ip interface info from xml to dict format - base_xml_to_dict: - cli_responses: "{{ item.stdout_show_ip }}" - with_items: - - "{{ output_bgp }}" - register: show_ip_intf_list - - name: call lib for bgp validation - bgp_validate: - show_ip_bgp: "{{ show_bgp_list.results }}" - show_ip_intf_brief: "{{ show_ip_intf_list.results }}" - bgp_neighbors: "{{ intended_bgp_neighbors }}" -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from collections import OrderedDict -import traceback - - -class BgpValidation(object): - def __init__(self): - self.module = AnsibleModule(argument_spec=self.get_fields()) - self.show_ip_bgp = self.module.params['show_ip_bgp'] - self.show_ip_intf_brief = self.module.params['show_ip_intf_brief'] - self.bgp_neighbors = self.module.params['bgp_neighbors'] - self.exit_msg = OrderedDict() - - def get_fields(self): - spec_fields = { - 'show_ip_bgp': { - 'type': 'list', - 'required': True - }, - 'show_ip_intf_brief': { - 'type': 'list', - 'required': True - }, - 'bgp_neighbors': { - 'type': 'list', - 'required': True - } - } - return spec_fields - - def parse_bgp_output(self): - show_bgp_dict = {} - for show_list in self.show_ip_bgp: - show_bgp_list = list() - item = show_list.get("item") - inv_name = None - if item is not None: - inv_name = item.get("inv_name") - msg = show_list.get("msg") - if msg is not None: - result = msg.get("result") - if result is not None: - for sub_result in result: - bgp_dict = {} - rpc_reply = sub_result.get("rpc-reply") - if rpc_reply is not None: - bulk = rpc_reply.get("bulk") - if bulk is not None: - data = bulk.get("data") - if data is not None and "peer-oper" in data: - peer_oper = data.get("peer-oper") - if peer_oper is not None and "remote-address" in peer_oper: - bgp_dict["remote_address"] = peer_oper.get( - "remote-address") - bgp_dict["bgp-state"] = peer_oper.get( - "bgp-state") - show_bgp_list.append(bgp_dict) - show_bgp_dict[inv_name] = show_bgp_list - return show_bgp_dict - - def parse_ip_intf_output(self): - show_ip_dict = {} - for show_list in self.show_ip_intf_brief: - show_ip_list = list() - item = show_list.get("item") - inv_name = None - if item is not None: - inv_name = item.get("inv_name") - msg = show_list.get("msg") - if msg is not None: - result = msg.get("result") - if result is not None: - for sub_result in result: - rpc_reply = sub_result.get("rpc-reply") - if rpc_reply is not None: - bulk = rpc_reply.get("bulk") - if bulk is not None: - data = bulk.get("data") - if data is not None: - sub_val = data.get("interface") - if sub_val is not None: - for val in sub_val: - intf_dict = {} - if "ipv4-info" in val: - ipv4_info = val.get( - "ipv4-info") - if ipv4_info is not None and "addr" in ipv4_info: - intf_dict["address"] = ipv4_info.get( - "addr") - intf_dict["if_name"] = val.get( - "name") - intf_dict["oper_status"] = val.get( - "oper-status") - if bool(intf_dict): - show_ip_list.append(intf_dict) - show_ip_dict[inv_name] = show_ip_list - return show_ip_dict - - def get_intf_info_per_ip(self, intf_dict): - final_intf_dict = {} - for key1, value1 in intf_dict.items(): - intf_list = value1 - intf_dict = {} - for ip in intf_list: - intf_info = {} - ip_address = ip.get("address") - intf_address = ip_address.split('/') - intf_ip = intf_address[0] - intf_info["if_name"] = ip.get("if_name") - intf_info["oper_status"] = ip.get("oper_status") - intf_info["dest_switch"] = key1 - intf_dict[intf_ip] = intf_info - if bool(intf_dict): - final_intf_dict[key1] = intf_dict - return final_intf_dict - - def get_intf_info_from_neighbor_ip( - self, source_switch, neighbor_ip, intf_dict): - final_intf_info = {} - intf_dict_per_ip = self.get_intf_info_per_ip(intf_dict) - for key, value in intf_dict_per_ip.items(): - switch_name = key - if source_switch == switch_name: - continue - intf_info = value.get(neighbor_ip) - if intf_info is None: - continue - final_intf_info = intf_info - break - return final_intf_info - - def get_bgp_final_nbr_list(self, bgp_dict, intf_dict): - actual_bgp_dict = {} - final_bgp_dict = {} - for key, value in bgp_dict.items(): - actual_bgp_list = list() - bgp_list = value - source_switch = key - for bgp in bgp_list: - final_dict = {} - bgp_state = bgp.get("bgp-state") - remote_address = bgp.get("remote_address") - reason = "neighbor config missing" - error_type = "config_missing" - intf_info = self.get_intf_info_from_neighbor_ip( - source_switch, remote_address, intf_dict) - if bool(intf_info): - dest_switch = intf_info.get("dest_switch") - remote_port = intf_info.get("if_name") - oper_status = intf_info.get("oper_status") - final_dict["source_switch"] = source_switch - final_dict["bgp_neighbor"] = remote_address - final_dict["bgp_state"] = bgp_state - if bgp_state != "established": - if oper_status != "up": - reason = ( - "remote port {0} {1} is {2}" .format( - dest_switch, remote_port, oper_status)) - error_type = "remote_port_down" - final_dict["error_type"] = error_type - final_dict["possible_reason"] = reason - else: - final_dict["source_switch"] = source_switch - final_dict["bgp_neighbor"] = remote_address - final_dict["bgp_state"] = bgp_state - final_dict["error_type"] = error_type - final_dict["possible_reason"] = reason - actual_bgp_list.append(final_dict) - actual_bgp_dict[source_switch] = actual_bgp_list - # check actual with intended neighbor to display the result - intended_list = list() - for intended_bgp_neighbor in self.bgp_neighbors: - planned_source_switch = intended_bgp_neighbor.get("source_switch") - planned_nbr_list = intended_bgp_neighbor.get("neighbor_ip") - actual_nbr_list = actual_bgp_dict.get(planned_source_switch) - if planned_nbr_list is None or actual_nbr_list is None: - continue - for actual_nbr in actual_nbr_list: - actual_source_switch = actual_nbr.get("source_switch") - actual_bgp_neighbor = actual_nbr.get("bgp_neighbor") - actual_bgp_state = actual_nbr.get("bgp_state") - if actual_bgp_neighbor in planned_nbr_list: - # Don't add established neighbor in result - if actual_bgp_state != "established": - intended_list.append(actual_nbr) - planned_nbr_list.remove(actual_bgp_neighbor) - else: - reason = "neighbor {0} is not an intended, please add this neighbor in the intended_bgp_neighbors".format( - actual_bgp_neighbor) - actual_nbr["bgp_neighbor"] = "-" - actual_nbr["error_type"] = "not_an_intended_neighbor" - actual_nbr["possible_reason"] = reason - intended_list.append(actual_nbr) - # Add the missed planned info which are not present in actual - # results - for planned_nbr in planned_nbr_list: - reason = "neighbor config missing" - temp_dict = {} - temp_dict["source_switch"] = planned_source_switch - temp_dict["bgp_neighbor"] = planned_nbr - temp_dict["error_type"] = "config_missing" - temp_dict["possible_reason"] = reason - intended_list.append(temp_dict) - return intended_list - - def perform_action(self): - try: - bgp_dict = self.parse_bgp_output() - intf_dict = self.parse_ip_intf_output() - final_bgp_list = self.get_bgp_final_nbr_list(bgp_dict, intf_dict) - self.exit_msg.update({"results": final_bgp_list}) - self.module.exit_json(changed=False, msg=self.exit_msg) - except Exception as e: - self.module.fail_json( - msg=to_native(e), - exception=traceback.format_exc()) - - -def main(): - module_instance = BgpValidation() - module_instance.perform_action() - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/dellemc/os10/plugins/modules/mtu_validate.py b/ansible_collections/dellemc/os10/plugins/modules/mtu_validate.py deleted file mode 100644 index f0a9620d2..000000000 --- a/ansible_collections/dellemc/os10/plugins/modules/mtu_validate.py +++ /dev/null @@ -1,220 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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) - -__copyright__ = "(c) 2020 Dell Inc. or its subsidiaries. All rights reserved." - -__metaclass__ = type - -DOCUMENTATION = ''' -module: mtu_validate -author: "Senthil Kumar Ganesan (@skg-net)" -short_description: Validate the MTU value for lldp neighbors -description: - - - Get the wiring info using lldp output and show system network summary. - -options: - show_lldp_neighbors_list: - description: - - show lldp neighbor output - type: 'list' - required: True - show_system_network_summary: - description: - - show system network summary output - type: 'list' - required: True - show_ip_intf_brief: - description: - - show ip intf brief - type: 'list' - required: True -''' -EXAMPLES = ''' -Copy below YAML into a playbook (e.g. play.yml) and run follows: - -#$ ansible-playbook -i inv play.yml -name: show mtu mismatch info -hosts: localhost -connection: local -gather_facts: False -tasks: - - name: "Get Dell EMC OS10 MTU mismatch info" - os10_command: - commands: - - command: "show lldp neighbors" - - command: "show ip interface brief | display-xml" - provider: "{{ hostvars[item].cli }}" - with_items: "{{ groups['all'] }}" - register: show_lldp - - set_fact: - output: "{{ output|default([])+ [{'host': item.invocation.module_args.provider.host, 'inv_name': item.item, - 'stdout_show_lldp': item.stdout.0, 'stdout_show_ip': item.stdout.1 }] }}" - loop: "{{ show_lldp.results }}" - - debug: var=output - - local_action: copy content={{ output }} dest=show1 - - name: call lib to convert ip interface info from xml to dict format - base_xml_to_dict: - cli_responses: "{{ item.stdout_show_ip }}" - with_items: "{{ output }}" - register: show_ip_intf_list - - local_action: copy content={{ show_ip_intf_list }} dest=show_ip - - - name: "Get Dell EMC OS10 Show system" - import_role: - name: os10_fabric_summary - register: show_system_network_summary - - debug: var=show_system_network_summary - - name: call lib to process - mtu_validate: - show_lldp_neighbors_list: "{{ output }}" - show_system_network_summary: "{{ show_system_network_summary.msg.results }}" - show_ip_intf_brief: "{{ show_ip_intf_list.results }}" -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from collections import OrderedDict -import re -import traceback - - -class MtuValidation(object): - def __init__(self): - self.module = AnsibleModule(argument_spec=self.get_fields()) - self.show_lldp_neighbors_list = self.module.params['show_lldp_neighbors_list'] - self.show_system_network_summary = self.module.params['show_system_network_summary'] - self.show_ip_intf_brief = self.module.params['show_ip_intf_brief'] - self.exit_msg = OrderedDict() - - def get_fields(self): - spec_fields = { - 'show_lldp_neighbors_list': { - 'type': 'list', - 'required': True - }, - 'show_system_network_summary': { - 'type': 'list', - 'required': True - }, - 'show_ip_intf_brief': { - 'type': 'list', - 'required': True - } - } - return spec_fields - - def get_int_mtu(self, spine, port): - for show_list in self.show_ip_intf_brief: - inv_name = show_list["item"]["inv_name"] - if spine != inv_name: - continue - value = show_list["msg"]["result"] - for data in value: - intf_list = data["rpc-reply"]["bulk"]["data"]["interface"] - for val in intf_list: - intf_name = val["name"] - if intf_name == port: - mtu = val["mtu"] - return mtu - return None - - # form actual neighbors per network with help of lldp output and show - # sytem output - def get_actual_neigbor(self, lldp_list): - final_out = list() - for show_system in self.show_system_network_summary: - for lldp in lldp_list: - if show_system["host"] != lldp["host"] and "node-mac" in show_system and "rem_mac" in lldp: - rem_host = show_system["host"] - loc_host = lldp["host"] - # check whether lldp output mac match with system summary - # mac and collect port and host info - dest_switch = show_system["inv_name"] - source_switch = lldp["inv_name"] - lldp_mac = lldp["rem_mac"] - for index, rem_mac in enumerate(lldp_mac): - final_dict = {} - if (str.lower( - show_system["node-mac"])) == (str.lower(rem_mac)): - final_dict["source_switch"] = source_switch - final_dict["dest_switch"] = dest_switch - final_dict["source_port"] = lldp["loc_port"][index] - final_dict["dest_port"] = lldp["rem_port"][index] - source_mtu = self.get_int_mtu( - source_switch, final_dict["source_port"]) - dest_mtu = self.get_int_mtu( - dest_switch, final_dict["dest_port"]) - if source_mtu is not None: - final_dict["source_mtu"] = source_mtu - if dest_mtu is not None: - final_dict["dest_mtu"] = dest_mtu - final_out.append(final_dict) - return final_out - - def parse_lldp_output(self): - nbr_list = list() - for cli in self.show_lldp_neighbors_list: - out_dict = {} - loc_port = list() - rem_port = list() - rem_mac = list() - for key, value in cli.items(): - if key == "host": - out_dict[key] = value - if key == "inv_name": - out_dict[key] = value - if key == "stdout_show_lldp": - output = str(value) - lldp_regexp = r"(\S+)\s+(\S+)\s+(\S+)\s+(\S+)" - lines = output.splitlines() - for line in lines: - if "Loc PortID" in line: - continue - match = re.match(lldp_regexp, line) - if match: - val = match.groups() - loc_port.append(val[0]) - rem_port.append(val[2]) - rem_mac.append(val[3]) - out_dict["loc_port"] = loc_port - out_dict["rem_port"] = rem_port - out_dict["rem_mac"] = rem_mac - nbr_list.append(out_dict) - return nbr_list - - def get_mtu_mismatch_info(self, nbr_list): - mtu_list = list() - for nbr in nbr_list: - if nbr["source_mtu"] != nbr["dest_mtu"]: - nbr["error"] = "mismatch" - mtu_list.append(nbr) - return mtu_list - - def perform_action(self): - try: - lldp_list = self.parse_lldp_output() - actual_nbr = self.get_actual_neigbor(lldp_list) - mtu_mismatch_list = self.get_mtu_mismatch_info(actual_nbr) - if len(mtu_mismatch_list) > 0: - self.exit_msg.update({"results": mtu_mismatch_list}) - else: - self.exit_msg.update( - {"results": "There is no MTU mistmatch between neighbors"}) - self.module.exit_json(changed=False, msg=self.exit_msg) - except Exception as e: - self.module.fail_json( - msg=to_native(e), - exception=traceback.format_exc()) - - -def main(): - module_instance = MtuValidation() - module_instance.perform_action() - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/dellemc/os10/plugins/modules/os10_command.py b/ansible_collections/dellemc/os10/plugins/modules/os10_command.py deleted file mode 100644 index a99f1a67f..000000000 --- a/ansible_collections/dellemc/os10/plugins/modules/os10_command.py +++ /dev/null @@ -1,229 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Peter Sprygada <psprygada@ansible.com> -# Copyright: (c) 2020, Dell Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = """ ---- -module: os10_command -author: "Senthil Kumar Ganesan (@skg-net)" -short_description: Run commands on devices running Dell EMC SmartFabric OS10 -description: - - Sends arbitrary commands to a OS10 device 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. - - This module does not support running commands in configuration mode. - Please use M(dellemc.os10.os10_config) to configure OS10 devices. -extends_documentation_fragment: dellemc.os10.os10 -options: - commands: - description: - - List of commands to send to the remote OS10 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. - 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 I(retries), the task fails. - See examples. - type: list - elements: str - 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: [ all, any ] - retries: - description: - - Specifies the number of retries a command should be 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 - os10_command: - commands: show version - - - name: run show version and check to see if output contains OS10 - os10_command: - commands: show version - wait_for: result[0] contains OS10 - - - name: run multiple commands on remote nodes - os10_command: - commands: - - show version - - show interface - - - name: run multiple commands and evaluate the output - os10_command: - commands: - - show version - - show interface - wait_for: - - result[0] contains OS10 - - result[1] contains Ethernet -""" - -RETURN = """ -stdout: - description: The set of responses from the commands - returned: always apart from low level errors (such as action plugin) - type: list - sample: ['...', '...'] -stdout_lines: - description: The value of stdout split into a list - returned: always apart from low level errors (such as action plugin) - type: list - sample: [['...', '...'], ['...'], ['...']] -failed_conditions: - description: The list of conditionals that have failed - returned: failed - type: list - sample: ['...', '...'] -warnings: - description: The list of warnings (if any) generated by module based on arguments - returned: always - type: list - sample: ['...', '...'] -""" -import time - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.dellemc.os10.plugins.module_utils.network.os10 import run_commands -from ansible_collections.dellemc.os10.plugins.module_utils.network.os10 import os10_argument_spec, check_args -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ComplexList -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Conditional -from ansible.module_utils.six import string_types - - -def to_lines(stdout): - for item in stdout: - if isinstance(item, string_types): - item = str(item).split('\n') - yield item - - -def parse_commands(module, warnings): - command = ComplexList(dict( - command=dict(key=True), - prompt=dict(), - answer=dict() - ), module) - commands = command(module.params['commands']) - for index, item in enumerate(commands): - if module.check_mode and not item['command'].startswith('show'): - warnings.append( - 'only show commands are supported when using check mode, not ' - 'executing `%s`' % item['command'] - ) - elif item['command'].startswith('conf'): - module.fail_json( - msg='os10_command does not support running config mode ' - 'commands. Please use os10_config instead' - ) - return commands - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - # { command: <str>, prompt: <str>, response: <str> } - commands=dict(type='list', required=True), - - wait_for=dict(type='list', elements='str'), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - argument_spec.update(os10_argument_spec) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - check_args(module, warnings) - 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/ansible_collections/dellemc/os10/plugins/modules/os10_config.py b/ansible_collections/dellemc/os10/plugins/modules/os10_config.py deleted file mode 100644 index 925568f14..000000000 --- a/ansible_collections/dellemc/os10/plugins/modules/os10_config.py +++ /dev/null @@ -1,346 +0,0 @@ -#!/usr/bin/python -# -# (c) 2020 Peter Sprygada, <psprygada@ansible.com> -# Copyright (c) 2020 Dell Inc. -# -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = """ ---- -module: os10_config -author: "Senthil Kumar Ganesan (@skg-net)" -short_description: Manage Dell EMC SmartFabric OS10 configuration sections -description: - - OS10 configurations use a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with OS10 configuration sections in - a deterministic way. -extends_documentation_fragment: dellemc.os10.os10 -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. This argument is mutually exclusive with I(src). - 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). - type: path - 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 - default: line - choices: ['line', 'strict', 'exact', 'none'] - 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'] - update: - description: - - The I(update) argument controls how the configuration statements - are processed on the remote device. Valid choices for the I(update) - argument are I(merge) and I(check). When you set this argument to - I(merge), the configuration changes merge with the current - device running configuration. When you set this argument to I(check) - the configuration updates are determined but not actually configured - on the remote device. - type: str - default: merge - choices: ['merge', 'check'] - save: - description: - - The C(save) argument instructs the module to save the running- - config to the startup-config at the conclusion of the module - running. If check mode is specified, this argument is ignored. - type: bool - default: 'no' - 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(config) argument allows the - implementer to pass in the configuration to use as the base - config for comparison. - type: str - 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. If the C(backup_options) value is not given, - the backup file is written to the C(backup) folder in the playbook - root directory. If the directory does not exist, it is created. - type: bool - default: 'no' - backup_options: - description: - - This is a dict object containing configurable options related to backup file path. - The value of this option is read only when C(backup) is set to I(yes), if C(backup) is set - to I(no) this option will be silently ignored. - suboptions: - filename: - description: - - The filename to be used to store the backup configuration. If the the filename - is not given it will be generated based on the hostname, current time and date - in format defined by <hostname>_config.<current-date>@<current-time> - type: str - dir_path: - description: - - This option provides the path ending with directory name in which the backup - configuration file will be stored. If the directory does not exist it will be first - created and the filename is either the value of C(filename) or default filename - as described in C(filename) options description. If the path value is not given - in that case a I(backup) directory will be created in the current working directory - and backup configuration will be copied in C(filename) within I(backup) directory. - type: path - type: dict -""" - -EXAMPLES = """ -- os10_config: - lines: ['hostname {{ inventory_hostname }}'] - -- os10_config: - lines: - - 10 permit ip host 1.1.1.1 any log - - 20 permit ip host 2.2.2.2 any log - - 30 permit ip host 3.3.3.3 any log - - 40 permit ip host 4.4.4.4 any log - - 50 permit ip host 5.5.5.5 any log - parents: ['ip access-list test'] - before: ['no ip access-list test'] - match: exact - -- os10_config: - lines: - - 10 permit ip host 1.1.1.1 any log - - 20 permit ip host 2.2.2.2 any log - - 30 permit ip host 3.3.3.3 any log - - 40 permit ip host 4.4.4.4 any log - parents: ['ip access-list test'] - before: ['no ip access-list test'] - replace: block - -- os10_config: - lines: ['hostname {{ inventory_hostname }}'] - backup: yes - backup_options: - filename: backup.cfg - dir_path: /home/user -""" - -RETURN = """ -updates: - description: The set of commands that will be pushed to the remote device. - returned: always - type: list - sample: ['hostname foo', 'router bgp 1', 'router-id 1.1.1.1'] -commands: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['hostname foo', 'router bgp 1', 'router-id 1.1.1.1'] -saved: - description: Returns whether the configuration is saved to the startup - configuration or not. - returned: When not check_mode. - type: bool - sample: True -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/os10_config.2016-07-16@22:28:34 -""" -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.dellemc.os10.plugins.module_utils.network.os10 import get_config, get_sublevel_config -from ansible_collections.dellemc.os10.plugins.module_utils.network.os10 import os10_argument_spec, check_args -from ansible_collections.dellemc.os10.plugins.module_utils.network.os10 import load_config, run_commands -from ansible_collections.dellemc.os10.plugins.module_utils.network.os10 import WARNING_PROMPTS_RE -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps - - -def get_candidate(module): - candidate = NetworkConfig(indent=1) - if module.params['src']: - candidate.load(module.params['src']) - elif module.params['lines']: - parents = module.params['parents'] or list() - commands = module.params['lines'][0] - if (isinstance(commands, dict)) and (isinstance((commands['command']), list)): - candidate.add(commands['command'], parents=parents) - elif (isinstance(commands, dict)) and (isinstance((commands['command']), str)): - candidate.add([commands['command']], parents=parents) - else: - candidate.add(module.params['lines'], parents=parents) - return candidate - - -def get_running_config(module): - contents = module.params['config'] - if not contents: - contents = get_config(module) - return contents - - -def main(): - - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - src=dict(type='path'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', - choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block']), - - update=dict(choices=['merge', 'check'], default='merge'), - save=dict(type='bool', default=False), - config=dict(), - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec) - ) - - argument_spec.update(os10_argument_spec) - - mutually_exclusive = [('lines', 'src')] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - parents = module.params['parents'] or list() - - match = module.params['match'] - replace = module.params['replace'] - - warnings = list() - check_args(module, warnings) - - result = dict(changed=False, saved=False, warnings=warnings) - - if module.params['backup']: - if not module.check_mode: - result['__backup__'] = get_config(module) - - commands = list() - candidate = get_candidate(module) - - if any((module.params['lines'], module.params['src'])): - if match != 'none': - config = get_running_config(module) - if parents: - contents = get_sublevel_config(config, module) - config = NetworkConfig(contents=contents, indent=1) - else: - config = NetworkConfig(contents=config, indent=1) - configobjs = candidate.difference(config, match=match, replace=replace) - else: - configobjs = candidate.items - - if configobjs: - commands = dumps(configobjs, 'commands') - if ((isinstance((module.params['lines']), list)) and - (isinstance((module.params['lines'][0]), dict)) and - (set(['prompt', 'answer']).issubset(module.params['lines'][0]))): - - cmd = {'command': commands, - 'prompt': module.params['lines'][0]['prompt'], - 'answer': module.params['lines'][0]['answer']} - commands = [module.jsonify(cmd)] - else: - commands = commands.split('\n') - - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - if not module.check_mode and module.params['update'] == 'merge': - load_config(module, commands) - - result['changed'] = True - result['commands'] = commands - result['updates'] = commands - - if module.params['save']: - result['changed'] = True - if not module.check_mode: - cmd = {r'command': 'copy running-config startup-config', - r'prompt': r'\[confirm yes/no\]:\s?$', 'answer': 'yes'} - run_commands(module, [cmd]) - result['saved'] = True - else: - module.warn('Skipping command `copy running-config startup-config`' - 'due to check_mode. Configuration not copied to ' - 'non-volatile storage') - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/dellemc/os10/plugins/modules/os10_facts.py b/ansible_collections/dellemc/os10/plugins/modules/os10_facts.py deleted file mode 100644 index c124422bd..000000000 --- a/ansible_collections/dellemc/os10/plugins/modules/os10_facts.py +++ /dev/null @@ -1,505 +0,0 @@ -#!/usr/bin/python -# -# (c) 2020 Peter Sprygada, <psprygada@ansible.com> -# Copyright (c) 2020 Dell Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = """ ---- -module: os10_facts -author: "Senthil Kumar Ganesan (@skg-net)" -short_description: Collect facts from devices running Dell EMC SmartFabric OS10 -description: - - Collects a base set of device facts from a remote device that - is running OS10. 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. -extends_documentation_fragment: dellemc.os10.os10 -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. - type: list - default: [ '!config' ] -""" - -EXAMPLES = """ -# Collect all facts from the device -- os10_facts: - gather_subset: all - -# Collect only the config and default facts -- os10_facts: - gather_subset: - - config - -# Do not collect hardware facts -- os10_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_name: - description: The name of the OS that is running. - returned: Always. - type: str -ansible_net_version: - description: The operating system version running on the remote device - returned: always - type: str -ansible_net_servicetag: - description: The service tag number of the remote device. - returned: always - type: str -ansible_net_model: - description: The model name returned from the device. - returned: always - type: str -ansible_net_hostname: - description: The configured hostname of the device - returned: always - type: str - -# hardware -ansible_net_cpu_arch: - description: CPU Architecture of the remote device. - returned: when hardware is configured - type: str -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 - -try: - from lxml import etree as ET -except ImportError: - import xml.etree.ElementTree as ET - -from ansible_collections.dellemc.os10.plugins.module_utils.network.os10 import run_commands -from ansible_collections.dellemc.os10.plugins.module_utils.network.os10 import os10_argument_spec, check_args -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - - -class FactsBase(object): - - COMMANDS = [] - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, self.COMMANDS, check_rc=False) - - def run(self, cmd): - return run_commands(self.module, cmd, check_rc=False) - - -class Default(FactsBase): - - COMMANDS = [ - 'show version | display-xml', - 'show system | display-xml', - ] - - def populate(self): - super(Default, self).populate() - data = self.responses[0] - xml_data = ET.fromstring(data.encode('utf8')) - - self.facts['name'] = self.parse_name(xml_data) - self.facts['version'] = self.parse_version(xml_data) - self.facts['model'] = self.parse_model(xml_data) - self.facts['hostname'] = self.parse_hostname(xml_data) - - data = self.responses[1] - xml_data = ET.fromstring(data.encode('utf8')) - - self.facts['servicetag'] = self.parse_servicetag(xml_data) - - def parse_name(self, data): - sw_name = data.find('./data/system-sw-state/sw-version/sw-name') - if sw_name is not None: - return sw_name.text - else: - return "" - - def parse_version(self, data): - sw_ver = data.find('./data/system-sw-state/sw-version/sw-version') - if sw_ver is not None: - return sw_ver.text - else: - return "" - - def parse_hostname(self, data): - hostname = data.find('./data/system-state/system-status/hostname') - if hostname is not None: - return hostname.text - else: - return "" - - def parse_model(self, data): - prod_name = data.find('./data/system-sw-state/sw-version/sw-platform') - if prod_name is not None: - return prod_name.text - else: - return "" - - def parse_servicetag(self, data): - svc_tag = data.find('./data/system/node/unit/mfg-info/service-tag') - if svc_tag is not None: - return svc_tag.text - else: - return "" - - -class Hardware(FactsBase): - - COMMANDS = [ - 'show version | display-xml', - 'show processes node-id 1 | grep "Mem :"' - ] - - def populate(self): - - super(Hardware, self).populate() - data = self.responses[0] - - xml_data = ET.fromstring(data.encode('utf8')) - - self.facts['cpu_arch'] = self.parse_cpu_arch(xml_data) - - data = self.responses[1] - match = self.parse_memory(data) - if match: - self.facts['memtotal_mb'] = int(match[0]) // 1024 - self.facts['memfree_mb'] = int(match[1]) // 1024 - - def parse_cpu_arch(self, data): - cpu_arch = data.find('./data/system-sw-state/sw-version/cpu-arch') - if cpu_arch is not None: - return cpu_arch.text - else: - return "" - - def parse_memory(self, data): - return re.findall(r'(\d+)', data, re.M) - - -class Config(FactsBase): - - COMMANDS = ['show running-config'] - - def populate(self): - super(Config, self).populate() - self.facts['config'] = self.responses[0] - - -class Interfaces(FactsBase): - - COMMANDS = [ - 'show interface | display-xml', - 'show lldp neighbors | display-xml' - ] - - def __init__(self, module): - self.intf_facts = dict() - self.lldp_facts = dict() - super(Interfaces, self).__init__(module) - - def populate(self): - super(Interfaces, self).populate() - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - - int_show_data = (self.responses[0]).splitlines() - pattern = '?xml version' - data = '' - skip = True - - # The output returns multiple xml trees - # parse them before handling. - for line in int_show_data: - if pattern in line: - if skip is False: - xml_data = ET.fromstring(data.encode('utf8')) - self.populate_interfaces(xml_data) - data = '' - else: - skip = False - - data += line - - if skip is False: - xml_data = ET.fromstring(data.encode('utf8')) - self.populate_interfaces(xml_data) - - self.facts['interfaces'] = self.intf_facts - - lldp_data = (self.responses[1]).splitlines() - data = '' - skip = True - # The output returns multiple xml trees - # parse them before handling. - for line in lldp_data: - if pattern in line: - if skip is False: - xml_data = ET.fromstring(data.encode('utf8')) - self.populate_neighbors(xml_data) - data = '' - else: - skip = False - - data += line - - if skip is False: - xml_data = ET.fromstring(data.encode('utf8')) - self.populate_neighbors(xml_data) - - self.facts['neighbors'] = self.lldp_facts - - def populate_interfaces(self, interfaces): - - for interface in interfaces.findall('./data/interfaces/interface'): - intf = dict() - name = self.parse_item(interface, 'name') - - intf['description'] = self.parse_item(interface, 'description') - intf['duplex'] = self.parse_item(interface, 'duplex') - intf['primary_ipv4'] = self.parse_primary_ipv4(interface) - intf['secondary_ipv4'] = self.parse_secondary_ipv4(interface) - intf['ipv6'] = self.parse_ipv6_address(interface) - intf['mtu'] = self.parse_item(interface, 'mtu') - intf['type'] = self.parse_item(interface, 'type') - - self.intf_facts[name] = intf - - for interface in interfaces.findall('./bulk/data/interface'): - name = self.parse_item(interface, 'name') - try: - intf = self.intf_facts[name] - intf['bandwidth'] = self.parse_item(interface, 'speed') - intf['adminstatus'] = self.parse_item(interface, 'admin-status') - intf['operstatus'] = self.parse_item(interface, 'oper-status') - intf['macaddress'] = self.parse_item(interface, 'phys-address') - except KeyError: - # skip the reserved interfaces - pass - - for interface in interfaces.findall('./data/ports/ports-state/port'): - name = self.parse_item(interface, 'name') - # media-type name interface name format phy-eth 1/1/1 - mediatype = self.parse_item(interface, 'media-type') - - typ, sname = name.split('-eth') - name = "ethernet" + sname - try: - intf = self.intf_facts[name] - intf['mediatype'] = mediatype - except Exception: - # fanout - for subport in range(1, 5): - name = "ethernet" + sname + ":" + str(subport) - try: - intf = self.intf_facts[name] - intf['mediatype'] = mediatype - except Exception: - # valid case to handle 2x50G - pass - - 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_item(self, interface, item): - elem = interface.find(item) - if elem is not None: - return elem.text - else: - return "" - - def parse_primary_ipv4(self, interface): - ipv4 = interface.find('ipv4') - ip_address = "" - if ipv4 is not None: - prim_ipaddr = ipv4.find('./address/primary-addr') - if prim_ipaddr is not None: - ip_address = prim_ipaddr.text - self.add_ip_address(ip_address, 'ipv4') - - return ip_address - - def parse_secondary_ipv4(self, interface): - ipv4 = interface.find('ipv4') - ip_address = "" - if ipv4 is not None: - sec_ipaddr = ipv4.find('./address/secondary-addr') - if sec_ipaddr is not None: - ip_address = sec_ipaddr.text - self.add_ip_address(ip_address, 'ipv4') - - return ip_address - - def parse_ipv6_address(self, interface): - - ip_address = list() - - for addr in interface.findall('./ipv6/ipv6-addresses/address'): - - ipv6_addr = addr.find('./ipv6-address') - - if ipv6_addr is not None: - ip_address.append(ipv6_addr.text) - self.add_ip_address(ipv6_addr.text, 'ipv6') - - return ip_address - - def populate_neighbors(self, interfaces): - for interface in interfaces.findall('./bulk/data/interface'): - name = interface.find('name').text - rem_sys_name = interface.find('./lldp-rem-neighbor-info/info/rem-system-name') - if rem_sys_name is not None: - self.lldp_facts[name] = list() - fact = dict() - fact['host'] = rem_sys_name.text - rem_sys_port = interface.find('./lldp-rem-neighbor-info/info/rem-lldp-port-id') - fact['port'] = rem_sys_port.text - self.lldp_facts[name].append(fact) - - -FACT_SUBSETS = dict( - default=Default, - hardware=Hardware, - interfaces=Interfaces, - config=Config, -) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - gather_subset=dict(default=['!config'], type='list') - ) - - argument_spec.update(os10_argument_spec) - - 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 - - warnings = list() - check_args(module, warnings) - - module.exit_json(ansible_facts=ansible_facts, warnings=warnings) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/dellemc/os10/plugins/modules/show_system_network_summary.py b/ansible_collections/dellemc/os10/plugins/modules/show_system_network_summary.py deleted file mode 100644 index 9922b9f8b..000000000 --- a/ansible_collections/dellemc/os10/plugins/modules/show_system_network_summary.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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) - -__copyright__ = "(c) 2020 Dell Inc. or its subsidiaries. All rights reserved." - -__metaclass__ = type - -DOCUMENTATION = ''' -module: show_system_network_summary -author: "Senthil Kumar Ganesan (@skg-net)" -short_description: Operations for show_system_network output in json/yaml format. -description: - - - Get the show system inforamtion of a Leaf-Spine. - -options: - output_type: - type: str - description: - - json or yaml - - Default value is json - default: json - required: False - cli_responses: - type: list - required: True - description: - - show system command xml output -''' -EXAMPLES = ''' -Copy below YAML into a playbook (e.g. play.yml) and run as follows: - -#$ ansible-playbook -i inv show.yml -name: show system Configuration -hosts: localhost -connection: local -gather_facts: False -vars: - cli: - username: admin - password: admin -tasks: -- name: "Get Dell EMC OS10 Show system summary" - os10_command: - commands: ['show system | display-xml'] - provider: "{{ hostvars[item].cli }}" - with_items: "{{ groups['all'] }}" - register: show_system -- set_fact: - output: "{{ output|default([])+ [{'inv_name': item.item, 'host': item.invocation.module_args.provider.host, 'stdout_show_system': item.stdout}] }}" - loop: "{{ show_system.results }}" -- debug: var=output -- name: "show system network call to lib " - show_system_network_summary: - cli_responses: "{{ output}} " - output_type: "{{ output_method if output_method is defined else 'json' }}" - register: show_system_network_summary -- debug: var=show_system_network_summary -''' - -import re -from ansible_collections.dellemc.os10.plugins.module_utils.network.base_network_show import BaseNetworkShow - - -class ShowSystemNetworkSummary(BaseNetworkShow): - def __init__(self): - BaseNetworkShow.__init__(self) - self.cli_responses = self.module.params['cli_responses'] - self.output_type = self.module.params['output_type'] - self.changed = False - - def get_fields(self): - spec_fields = { - 'cli_responses': { - 'type': 'list', - 'required': True - }, - 'output_type': { - 'type': 'str', - 'default': "json", - 'required': False - } - } - return spec_fields - - def perform_action(self): - out = list() - show_system_summary = self.cli_responses - if len(show_system_summary) > 0: - for item in show_system_summary: - out_dict = {} - host = item.get("host") - inv_name = item.get("inv_name") - show_system_response = item.get("stdout_show_system") - if show_system_response is not None: - result = BaseNetworkShow.xml_to_dict( - self, show_system_response[0]) - rpc_reply = result.get("rpc-reply") - if rpc_reply is not None: - data = rpc_reply.get("data") - if data is not None: - out_dict["host"] = host - out_dict["inv_name"] = inv_name - system_state = data.get("system-state") - if system_state is not None: - system_status = system_state.get( - "system-status") - if system_status is not None: - out_dict["hostname"] = system_status.get( - "hostname") - system = data.get("system") - if system is not None: - node = system.get("node") - if node is not None: - out_dict["node-mac"] = node.get("node-mac") - unit = node.get("unit") - if unit is not None: - out_dict["software-version"] = unit.get( - "software-version") - mfg_info = node.get("mfg-info") - if mfg_info is not None: - out_dict["service-tag"] = mfg_info.get( - "service-tag") - out_dict["device type"] = mfg_info.get( - "product-name") - if bool(out_dict): - out.append(out_dict) - if self.output_type != "json": - self.exit_msg.update( - {"results": (BaseNetworkShow.dict_to_yaml(self, out))}) - else: - self.exit_msg.update({"results": (out)}) - self.module.exit_json(changed=False, msg=self.exit_msg) - - -def main(): - module_instance = ShowSystemNetworkSummary() - module_instance.perform_action() - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/dellemc/os10/plugins/modules/vlt_validate.py b/ansible_collections/dellemc/os10/plugins/modules/vlt_validate.py deleted file mode 100644 index 2042dfe77..000000000 --- a/ansible_collections/dellemc/os10/plugins/modules/vlt_validate.py +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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) - -__copyright__ = "(c) 2020 Dell Inc. or its subsidiaries. All rights reserved." - -__metaclass__ = type - -DOCUMENTATION = ''' -module: vlt_validate -author: "Senthil Kumar Ganesan (@skg-net)" -short_description: Validate the vlt info, raise an error if peer is not in up state -description: - - - Troubleshoot the show vlt info and raise an error if peer is not up. - -options: - show_vlt: - description: - - show vlt output - type: 'list' - required: True - show_system_network_summary: - description: - - show system summary output - type: 'list' - required: True - intended_vlt_pairs: - description: - - intended vlt pair intput to verify with actual - type: 'list' - required: True - -''' -EXAMPLES = ''' -Copy below YAML into a playbook (e.g. play.yml) and run as follows: - -#$ ansible-playbook -i inv play.yml -name: show system Configuration -hosts: localhost -connection: local -gather_facts: False -tasks: - - name: "Get Dell EMC OS10 Show run vlt" - os10_command: - commands: - - command: "show running-configuration vlt | grep vlt-domain" - provider: "{{ hostvars[item].cli }}" - with_items: "{{ groups['all'] }}" - register: show_run_vlt - - set_fact: - output_vlt: "{{ output_vlt|default([])+ [{'host': item.invocation.module_args.provider.host, 'inv_name': item.item, - 'stdout_show_vlt': item.stdout.0}] }}" - loop: "{{ show_run_vlt.results }}" - - debug: var=output_vlt - - name: "Get Dell EMC OS10 Show vlt info" - os10_command: - commands: - - command: "show vlt {{ item.stdout_show_vlt.split()[1] }} | display-xml" - provider: "{{ hostvars[item.inv_name].cli }}" - with_items: "{{ output_vlt }}" - register: show_vlt - - set_fact: - vlt_out: "{{ vlt_out|default([])+ [{'host': item.invocation.module_args.provider.host, 'inv_name': item.item, 'show_vlt_stdout': item.stdout.0}] }}" - loop: "{{ show_vlt.results }}" - register: vlt_output - - name: call lib to convert vlt info from xml to dict format - base_xml_to_dict: - cli_responses: "{{ item.show_vlt_stdout }}" - with_items: - - "{{ vlt_out }}" - register: vlt_dict_output - - name: "Get Dell EMC OS10 Show system" - import_role: - name: os10_fabric_summary - register: show_system_network_summary - - name: call lib to process - vlt_validate: - show_vlt : "{{ vlt_dict_output.results }}" - show_system_network_summary: "{{ show_system_network_summary.msg.results }}" - intended_vlt_pairs: "{{ intended_vlt_pairs }}" - register: show_vlt_info - -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from collections import OrderedDict -import traceback - - -class VltValidation(object): - def __init__(self): - self.module = AnsibleModule(argument_spec=self.get_fields()) - self.show_vlt = self.module.params['show_vlt'] - self.show_system_network_summary = self.module.params['show_system_network_summary'] - self.intended_vlt_pairs = self.module.params['intended_vlt_pairs'] - self.exit_msg = OrderedDict() - - def get_fields(self): - spec_fields = { - 'show_vlt': { - 'type': 'list', - 'required': True - }, - 'show_system_network_summary': { - 'type': 'list', - 'required': True - }, - 'intended_vlt_pairs': { - 'type': 'list', - 'required': True - } - } - return spec_fields - - # get switch inv name from mac - def get_switch_inv_name_from_mac(self, mac): - inv_name = None - for show_system in self.show_system_network_summary: - if (str.lower(show_system["node-mac"])) == (str.lower(mac)): - inv_name = show_system.get("inv_name") - break - return inv_name - - def validate_vlt_pairs(self, actual_vlt_dict): - final_out = list() - intended_vlt_list = self.intended_vlt_pairs - for intended_vlt in intended_vlt_list: - intended_primary = intended_vlt.get("primary") - intended_secondary = intended_vlt.get("secondary") - actual_vlt = actual_vlt_dict.get(intended_primary) - temp_dict = {} - if actual_vlt is not None: - actual_secondary = actual_vlt.get("secondary") - secondary_status = actual_vlt.get("secondary_status") - if actual_secondary is not None and intended_secondary != actual_secondary: - temp_dict["error_type"] = "secondary_mismatch" - temp_dict["intended_primary"] = intended_primary - temp_dict["intended_secondary"] = intended_secondary - temp_dict["secondary"] = actual_secondary - reason = "config mismatch as {0} is expected, but the actual secondary is {1} " .format( - intended_secondary, actual_secondary) - temp_dict["possible_reason"] = reason - final_out.append(temp_dict) - else: - if actual_secondary is None: - temp_dict["intended_primary"] = intended_primary - temp_dict["intended_secondary"] = intended_secondary - temp_dict["error_type"] = "peer_missing" - reason = "peer info is not configured or peer interface is down" - temp_dict["possible_reason"] = reason - final_out.append(temp_dict) - elif intended_secondary == actual_secondary and secondary_status != "up": - temp_dict["intended_primary"] = intended_primary - temp_dict["intended_secondary"] = intended_secondary - temp_dict["secondary"] = actual_secondary - temp_dict["error_type"] = "peer_down" - reason = "peer interface is down" - temp_dict["possible_reason"] = reason - final_out.append(temp_dict) - else: - temp_dict["intended_primary"] = intended_primary - temp_dict["intended_secondary"] = intended_secondary - temp_dict["error_type"] = "vlt_config_missing" - temp_dict["possible_reason"] = "vlt is not configured" - final_out.append(temp_dict) - return final_out - - def parse_vlt_output(self): - show_vlt_dict = {} - for show_list in self.show_vlt: - source_switch = None - item = show_list.get("item") - if item is not None: - inv_info = item.get("inv_name") - source_switch = inv_info.get("inv_name") - msg = show_list.get("msg") - if msg is not None: - result = msg.get("result") - for sub_result in result: - vlt_dict = {} - rpc_reply = sub_result.get("rpc-reply") - data = rpc_reply.get("data") - if data is not None: - topo_oper_data = data.get("topology-oper-data") - if topo_oper_data is not None: - vlt_domain = topo_oper_data.get("vlt-domain") - if vlt_domain is not None: - local_info = vlt_domain.get("local-info") - if local_info is not None: - local_role = local_info.get("role") - vlt_dict[local_role] = source_switch - local_mac = local_info.get("system-mac") - vlt_dict[local_role + "_mac"] = local_mac - peer_info = vlt_domain.get("peer-info") - if peer_info is not None: - peer_mac = peer_info.get("system-mac") - peer_switch = self.get_switch_inv_name_from_mac( - peer_mac) - peer_role = peer_info.get("role") - vlt_dict[peer_role] = peer_switch - vlt_dict[peer_role + "_mac"] = peer_mac - peer_status = peer_info.get("peer-status") - vlt_dict[peer_role + - "_status"] = peer_status - if bool(vlt_dict): - primary_switch = vlt_dict.get("primary") - vlt_data = show_vlt_dict.get(primary_switch) - if vlt_data is None: - # update database specific to primary, it helps - # to avoid to skip duplicate data - show_vlt_dict[primary_switch] = vlt_dict - return show_vlt_dict - - def perform_action(self): - try: - actual_vlt_dict = self.parse_vlt_output() - final_out = self.validate_vlt_pairs(actual_vlt_dict) - self.exit_msg.update({"results": final_out}) - self.module.exit_json(changed=False, msg=self.exit_msg) - except Exception as e: - self.module.fail_json( - msg=to_native(e), - exception=traceback.format_exc()) - - -def main(): - module_instance = VltValidation() - module_instance.perform_action() - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/dellemc/os10/plugins/modules/wiring_validate.py b/ansible_collections/dellemc/os10/plugins/modules/wiring_validate.py deleted file mode 100644 index 7947c1b19..000000000 --- a/ansible_collections/dellemc/os10/plugins/modules/wiring_validate.py +++ /dev/null @@ -1,246 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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) - -__copyright__ = "(c) 2020 Dell Inc. or its subsidiaries. All rights reserved." - -__metaclass__ = type - -DOCUMENTATION = ''' -module: wiring_validate -author: "Senthil Kumar Ganesan (@skg-net)" -short_description: Validate the wiring based on the planned wiring details -description: - - - Get the wiring info using lldp output and show system network summary. - -options: - show_lldp_neighbors_list: - description: - - show lldp neighbor output - type: 'list' - required: True - show_system_network_summary: - description: - - show system network summary output - type: 'list' - required: True - planned_neighbors: - description: - - planned neighbours input from group_var to compare actual - type: 'list' - required: True -''' -EXAMPLES = ''' -Copy below YAML into a playbook (e.g. play.yml) and run as follows: - -#$ ansible-playbook -i inv play.yml -name: show system Configuration -hosts: localhost -connection: local -gather_facts: False -tasks: -- name: "Get Dell EMC OS10 Show lldp" - os10_command: - commands: - - command: "show lldp neighbors" - provider: "{{ hostvars[item].cli }}" - with_items: "{{ groups['all'] }}" - register: show_lldp -- local_action: copy content={{ show_lldp }} dest=show -- set_fact: - output_lldp: "{{ output_lldp|default([])+ [{'host': item.invocation.module_args.provider.host, 'inv_name': item.item, - 'stdout_show_lldp': item.stdout}] }}" - loop: "{{ show_lldp.results }}" -- debug: var=output_lldp -- name: "Get Dell EMC OS10 Show system" - import_role: - name: os10_fabric_summary - register: show_system_network_summary -- debug: var=show_system_network_summary -- name: call lib to process - wiring_validate: - show_lldp_neighbors_list: "{{ output_lldp }}" - show_system_network_summary: "{{ show_system_network_summary.msg.results }}" - planned_neighbors: "{{ intended_neighbors }}" -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from collections import OrderedDict -import re -import traceback - - -class WiringValidation(object): - def __init__(self): - self.module = AnsibleModule(argument_spec=self.get_fields()) - self.show_lldp_neighbors_list = self.module.params['show_lldp_neighbors_list'] - self.show_system_network_summary = self.module.params['show_system_network_summary'] - self.planned_neighbors = self.module.params['planned_neighbors'] - self.exit_msg = OrderedDict() - - def get_fields(self): - spec_fields = { - 'show_lldp_neighbors_list': { - 'type': 'list', - 'required': True - }, - 'show_system_network_summary': { - 'type': 'list', - 'required': True - }, - 'planned_neighbors': { - 'type': 'list', - 'required': True - } - } - return spec_fields - - # get switch inv name from mac - def get_switch_inv_name_from_mac(self, mac): - inv_name = None - for show_system in self.show_system_network_summary: - if (str.lower(show_system["node-mac"])) == (str.lower(mac)): - inv_name = show_system.get("inv_name") - break - return inv_name - - # get service tag for switch - - def get_service_tag_and_mac(self): - svc_tag_mac = {} - for show_system in self.show_system_network_summary: - temp_dict = {} - temp_dict["svc-tag"] = show_system.get("service-tag") - temp_dict["node-mac"] = show_system.get("node-mac") - if bool(temp_dict): - svc_tag_mac[show_system["inv_name"]] = temp_dict - return svc_tag_mac - - # form actual neighbors per network with help of lldp output and show - # sytem output - def get_actual_neigbor(self, lldp_list): - final_out = list() - for lldp in lldp_list: - # check whether lldp output mac match with system summary mac and - # collect port and host info - source_switch = lldp["inv_name"] - lldp_mac = lldp["rem_mac"] - for index, rem_mac in enumerate(lldp_mac): - final_dict = {} - final_dict["source_switch"] = source_switch - final_dict["source_port"] = lldp["loc_port"][index] - final_dict["dest_port"] = lldp["rem_port"][index] - dest_switch = self.get_switch_inv_name_from_mac(rem_mac) - if dest_switch is not None: - final_dict["dest_switch"] = dest_switch - else: - final_dict["dest_switch"] = "unknown" - final_out.append(final_dict) - return final_out - - def parse_lldp_output(self): - nbr_list = list() - for item in self.show_lldp_neighbors_list: - out_dict = {} - loc_port = list() - rem_port = list() - rem_mac = list() - out_dict["host"] = item.get("host") - out_dict["inv_name"] = item.get("inv_name") - show_lldp_output = item.get("stdout_show_lldp") - if show_lldp_output is not None: - output = str(show_lldp_output[0]) - lldp_regexp = r"(\S+)\s+(\S+)\s+(\S+)\s+(\S+)" - lines = output.splitlines() - for line in lines: - if "Loc PortID" in line: - continue - match = re.match(lldp_regexp, line) - if match: - val = match.groups() - loc_port.append(val[0]) - rem_port.append(val[2]) - rem_mac.append(val[3]) - out_dict["loc_port"] = loc_port - out_dict["rem_port"] = rem_port - out_dict["rem_mac"] = rem_mac - if bool(out_dict): - nbr_list.append(out_dict) - return nbr_list - - def perform_action(self): - try: - lldp_list = self.parse_lldp_output() - actual_nbr = self.get_actual_neigbor(lldp_list) - svc_tag_mac = self.get_service_tag_and_mac() - # Validate the planned neighbors with actual neighbors - mismatch_list = list() - for planned_neighbors in self.planned_neighbors: - bflag = False - if planned_neighbors not in actual_nbr: - for actual_neighbors in actual_nbr: - if (actual_neighbors["source_switch"] == planned_neighbors["source_switch"] - and actual_neighbors["source_port"] == planned_neighbors["source_port"]): - if (actual_neighbors["dest_switch"] != - planned_neighbors["dest_switch"]): - bflag = True - if (actual_neighbors["dest_switch"] - != "unknown"): - reason = ( - "Destination switch is not an expected value, " - "expected switch: {0},port: {1}; actual switch: {2}(svc-tag:{3}, node_mac:{4}), port: {5}" .format( - planned_neighbors["dest_switch"], - planned_neighbors["dest_port"], - actual_neighbors["dest_switch"], - svc_tag_mac.get( - actual_neighbors["dest_switch"]).get("svc-tag"), - svc_tag_mac.get( - actual_neighbors["dest_switch"]).get("node-mac"), - actual_neighbors["dest_port"])) - else: - reason = ( - "Destination switch is not an expected value, " - "expected switch: {0},port: {1}; actual switch: {2}, port: {3}" .format( - planned_neighbors["dest_switch"], - planned_neighbors["dest_port"], - actual_neighbors["dest_switch"], - actual_neighbors["dest_port"])) - planned_neighbors["reason"] = reason - planned_neighbors["error_type"] = "link-mismatch" - break - if(actual_neighbors["dest_port"] != planned_neighbors["dest_port"]): - bflag = True - reason = ( - "Destination switch port is not an expected value, " - "expected port: {0} actual port: {1}" .format( - planned_neighbors["dest_port"], - actual_neighbors["dest_port"])) - planned_neighbors["reason"] = reason - planned_neighbors["error_type"] = "link-mismatch" - break - if not bflag: - reason = "link is not found for source switch: {0},port: {1}".format( - planned_neighbors["source_switch"], planned_neighbors["source_port"]) - planned_neighbors["reason"] = reason - planned_neighbors["error_type"] = "link-missing" - mismatch_list.append(planned_neighbors) - - self.exit_msg.update({"results": mismatch_list}) - self.module.exit_json(changed=False, msg=self.exit_msg) - except Exception as e: - self.module.fail_json( - msg=to_native(e), - exception=traceback.format_exc()) - - -def main(): - module_instance = WiringValidation() - module_instance.perform_action() - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/dellemc/os10/plugins/terminal/os10.py b/ansible_collections/dellemc/os10/plugins/terminal/os10.py deleted file mode 100644 index c3e1d3ac2..000000000 --- a/ansible_collections/dellemc/os10/plugins/terminal/os10.py +++ /dev/null @@ -1,81 +0,0 @@ -# -# (c) 2020 Red Hat Inc. -# -# This file is part of Ansible -# -# Copyright (c) 2020 Dell Inc. -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see <http://www.gnu.org/licenses/>. -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import re -import json - -from ansible.module_utils._text import to_text, to_bytes -from ansible.plugins.terminal import TerminalBase -from ansible.errors import AnsibleConnectionFailure - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:#) ?$"), - re.compile(br"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$") - ] - - terminal_stderr_re = [ - re.compile(br"% ?Error"), - re.compile(br"% ?Bad secret"), - re.compile(br"Syntax error:"), - re.compile(br"invalid input", re.I), - re.compile(br"(?:incomplete|ambiguous) command", re.I), - re.compile(br"connection timed out", re.I), - re.compile(br"[^\r\n]+ not found", re.I), - re.compile(br"'[^']' +returned error code: ?\d+"), - ] - - def on_open_shell(self): - try: - self._exec_cli_command(b'terminal length 0') - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to set terminal parameters') - - def on_become(self, passwd=None): - if self._get_prompt().endswith(b'#'): - return - - cmd = {u'command': u'enable'} - if passwd: - cmd[u'prompt'] = to_text(r"[\r\n]?password: $", errors='surrogate_or_strict') - cmd[u'answer'] = passwd - - try: - self._exec_cli_command(to_bytes(json.dumps(cmd), errors='surrogate_or_strict')) - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to elevate privilege to enable mode') - - def on_unbecome(self): - prompt = self._get_prompt() - if prompt is None: - # if prompt is None most likely the terminal is hung up at a prompt - return - - if prompt.strip().endswith(b')#'): - self._exec_cli_command(b'end') - self._exec_cli_command(b'disable') - - elif prompt.endswith(b'#'): - self._exec_cli_command(b'disable') |